1//! Provides core data types for language toolchains.
2//!
3//! A language can have associated toolchains,
4//! which is a set of tools used to interact with the projects written in said language.
5//! For example, a Python project can have an associated virtual environment; a Rust project can have a toolchain override.
6
7use std::{path::Path, sync::Arc};
8
9use gpui::SharedString;
10use util::rel_path::RelPath;
11
12use crate::{LanguageName, ManifestName};
13
14/// Represents a single toolchain.
15#[derive(Clone, Eq, Debug)]
16pub struct Toolchain {
17 /// User-facing label
18 pub name: SharedString,
19 /// Absolute path
20 pub path: SharedString,
21 pub language_name: LanguageName,
22 /// Full toolchain data (including language-specific details)
23 pub as_json: serde_json::Value,
24}
25
26impl std::hash::Hash for Toolchain {
27 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
28 let Self {
29 name,
30 path,
31 language_name,
32 as_json: _,
33 } = self;
34 name.hash(state);
35 path.hash(state);
36 language_name.hash(state);
37 }
38}
39
40impl PartialEq for Toolchain {
41 fn eq(&self, other: &Self) -> bool {
42 let Self {
43 name,
44 path,
45 language_name,
46 as_json: _,
47 } = self;
48 // Do not use as_json for comparisons; it shouldn't impact equality, as it's not user-surfaced.
49 // Thus, there could be multiple entries that look the same in the UI.
50 (name, path, language_name).eq(&(&other.name, &other.path, &other.language_name))
51 }
52}
53
54/// Declares a scope of a toolchain added by user.
55///
56/// When the user adds a toolchain, we give them an option to see that toolchain in:
57/// - All of their projects
58/// - A project they're currently in.
59/// - Only in the subproject they're currently in.
60#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
61pub enum ToolchainScope {
62 Subproject(Arc<Path>, Arc<RelPath>),
63 Project,
64 /// Available in all projects on this box. It wouldn't make sense to show suggestions across machines.
65 Global,
66}
67
68impl ToolchainScope {
69 pub fn label(&self) -> &'static str {
70 match self {
71 ToolchainScope::Subproject(_, _) => "Subproject",
72 ToolchainScope::Project => "Project",
73 ToolchainScope::Global => "Global",
74 }
75 }
76
77 pub fn description(&self) -> &'static str {
78 match self {
79 ToolchainScope::Subproject(_, _) => {
80 "Available only in the subproject you're currently in."
81 }
82 ToolchainScope::Project => "Available in all locations in your current project.",
83 ToolchainScope::Global => "Available in all of your projects on this machine.",
84 }
85 }
86}
87
88#[derive(Clone, PartialEq, Eq, Hash)]
89pub struct ToolchainMetadata {
90 /// Returns a term which we should use in UI to refer to toolchains produced by a given `ToolchainLister`.
91 pub term: SharedString,
92 /// A user-facing placeholder describing the semantic meaning of a path to a new toolchain.
93 pub new_toolchain_placeholder: SharedString,
94 /// The name of the manifest file for this toolchain.
95 pub manifest_name: ManifestName,
96}
97
98type DefaultIndex = usize;
99#[derive(Default, Clone, Debug)]
100pub struct ToolchainList {
101 pub toolchains: Vec<Toolchain>,
102 pub default: Option<DefaultIndex>,
103 pub groups: Box<[(usize, SharedString)]>,
104}
105
106impl ToolchainList {
107 pub fn toolchains(&self) -> &[Toolchain] {
108 &self.toolchains
109 }
110 pub fn default_toolchain(&self) -> Option<Toolchain> {
111 self.default.and_then(|ix| self.toolchains.get(ix)).cloned()
112 }
113 pub fn group_for_index(&self, index: usize) -> Option<(usize, SharedString)> {
114 if index >= self.toolchains.len() {
115 return None;
116 }
117 let first_equal_or_greater = self
118 .groups
119 .partition_point(|(group_lower_bound, _)| group_lower_bound <= &index);
120 self.groups
121 .get(first_equal_or_greater.checked_sub(1)?)
122 .cloned()
123 }
124}