1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
use crate::project_error::ProjectError;
use moon_common::{
Id, cacheable,
path::{WorkspaceRelativePathBuf, is_root_level_source},
};
use moon_config::{
DependencyScope, InheritedTasks, LanguageType, LayerType, ProjectConfig,
ProjectDependencyConfig, StackType,
};
use moon_file_group::FileGroup;
use moon_task::{Target, Task};
use std::collections::BTreeMap;
use std::fmt;
use std::path::PathBuf;
cacheable!(
#[derive(Clone, Debug, PartialEq)]
pub struct ProjectAlias {
pub alias: String,
pub plugin: Id,
}
);
impl AsRef<str> for ProjectAlias {
fn as_ref(&self) -> &str {
&self.alias
}
}
cacheable!(
#[derive(Clone, Debug, Default)]
#[serde(default)]
pub struct Project {
/// Unique aliases derived from plugins, typically toolchain manifests.
#[serde(skip_serializing_if = "Vec::is_empty")]
pub aliases: Vec<ProjectAlias>,
/// Project configuration loaded from "moon.*", if it exists.
pub config: ProjectConfig,
/// List of other projects this project depends on.
#[serde(skip_serializing_if = "Vec::is_empty")]
pub dependencies: Vec<ProjectDependencyConfig>,
/// File groups specific to the project. Inherits all file groups from the global config.
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
pub file_groups: BTreeMap<Id, FileGroup>,
/// Unique ID for the project. Is the LHS of the `projects` setting.
pub id: Id,
/// Task configuration that was inherited from ".moon/tasks".
#[serde(skip_serializing_if = "Option::is_none")]
pub inherited: Option<InheritedTasks>,
/// Primary programming language of the project.
pub language: LanguageType,
/// The type of layer within the stack. Is used for layer constraints.
pub layer: LayerType,
/// Absolute path to the project's root folder.
pub root: PathBuf,
/// Relative path from the workspace root to the project root.
pub source: WorkspaceRelativePathBuf,
/// The technology stack of the project.
pub stack: StackType,
/// Tasks specific to the project. Inherits all tasks from the global config.
/// Note: This map is empty when the project is in the project graph!
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
pub tasks: BTreeMap<Id, Task>,
/// List of targets of all tasks configured or inherited for the project.
/// Includes internal tasks!
#[serde(skip_serializing_if = "Vec::is_empty")]
pub task_targets: Vec<Target>,
/// Toolchains derived from the configured language.
#[serde(skip_serializing_if = "Vec::is_empty")]
pub toolchains: Vec<Id>,
}
);
impl Project {
/// Return a list of project IDs this project depends on.
pub fn get_dependency_ids(&self) -> Vec<&Id> {
self.dependencies
.iter()
.map(|dep| &dep.id)
.collect::<Vec<_>>()
}
/// Return a list of all toolchains that are enabled for this project.
/// Toolchains can be disabled through config.
pub fn get_enabled_toolchains(&self) -> Vec<&Id> {
self.toolchains
.iter()
.filter(|id| match self.config.toolchains.get_plugin_config(*id) {
None => true,
Some(cfg) => cfg.is_enabled(),
})
.collect()
}
/// Return a list of all task specific toolchains that are enabled for this project.
/// Toolchains can be disabled through config.
pub fn get_enabled_toolchains_for_task<'task>(&self, task: &'task Task) -> Vec<&'task Id> {
task.toolchains
.iter()
.filter(|id| match self.config.toolchains.get_plugin_config(*id) {
None => true,
Some(cfg) => cfg.is_enabled(),
})
.collect()
}
/// Return the file group by ID or fail with an error.
pub fn get_file_group(&self, group_id: impl AsRef<str>) -> Result<&FileGroup, ProjectError> {
self.file_groups
.get(group_id.as_ref())
.ok_or_else(|| ProjectError::UnknownFileGroup {
group_id: group_id.as_ref().to_string(),
project_id: self.id.to_string(),
})
}
/// Return the task by ID or fail with an error.
pub fn get_task(&self, task_id: impl AsRef<str>) -> Result<&Task, ProjectError> {
self.tasks
.get(task_id.as_ref())
.ok_or_else(|| ProjectError::UnknownTask {
task_id: task_id.as_ref().to_string(),
project_id: self.id.to_string(),
})
}
/// Return true if the root-level project.
pub fn is_root_level(&self) -> bool {
is_root_level_source(&self.source)
}
/// Return true if the provided locator string (an ID or alias) matches the
/// current project.
pub fn matches_locator(&self, locator: &str) -> bool {
self.id.as_str() == locator || self.aliases.iter().any(|alias| alias.alias == locator)
}
/// Convert the project into a fragment.
pub fn to_fragment(&self) -> ProjectFragment {
ProjectFragment {
aliases: self
.aliases
.iter()
.map(|alias| alias.alias.clone())
.collect(),
dependency_scope: None,
id: self.id.clone(),
source: self.source.to_string(),
toolchains: self.get_enabled_toolchains().into_iter().cloned().collect(),
}
}
}
impl PartialEq for Project {
fn eq(&self, other: &Self) -> bool {
self.aliases == other.aliases
&& self.file_groups == other.file_groups
&& self.id == other.id
&& self.language == other.language
&& self.layer == other.layer
&& self.root == other.root
&& self.source == other.source
&& self.stack == other.stack
&& self.tasks == other.tasks
&& self.task_targets == other.task_targets
}
}
impl fmt::Display for Project {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.id)
}
}
cacheable!(
/// Fragment of a project including important fields.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ProjectFragment {
/// Alias of the project.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub aliases: Vec<String>,
/// When treated as a dependency for another project,
/// the scope of that dependency relationship.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dependency_scope: Option<DependencyScope>,
/// ID of the project.
pub id: Id,
/// Workspace relative path to the project root.
pub source: String,
/// Toolchains the project belongs to. Does not include
/// toolchains that have been disabled through config.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub toolchains: Vec<Id>,
}
);