ic_build/cargo/selection/
mod.rs1use std::{
4 collections::BTreeSet,
5 ffi::OsString,
6 path::{Component, Path},
7};
8
9#[cfg(test)]
10mod tests;
11
12#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct CargoFeatureSelection {
15 pub features: BTreeSet<String>,
17 pub all_features: bool,
19 pub default_features: bool,
21}
22
23impl Default for CargoFeatureSelection {
24 fn default() -> Self {
25 Self {
26 features: BTreeSet::new(),
27 default_features: true,
28 all_features: false,
29 }
30 }
31}
32
33impl CargoFeatureSelection {
34 pub(crate) fn arguments(&self) -> Vec<OsString> {
35 let mut arguments = Vec::new();
36 if self.all_features {
37 arguments.push("--all-features".into());
38 }
39 if !self.default_features {
40 arguments.push("--no-default-features".into());
41 }
42 if !self.features.is_empty() {
43 arguments.push(
44 format!(
45 "--features={}",
46 self.features
47 .iter()
48 .map(String::as_str)
49 .collect::<Vec<_>>()
50 .join(",")
51 )
52 .into(),
53 );
54 }
55 arguments
56 }
57}
58
59#[derive(Clone, Debug, Default, Eq, PartialEq)]
67pub struct CargoSelection {
68 pub profile: Option<String>,
70 pub features: CargoFeatureSelection,
72 pub locked: bool,
74 pub offline: bool,
76}
77
78impl CargoSelection {
79 pub(crate) fn profile_target_dir(&self) -> Result<&str, &'static str> {
80 let profile = self.profile.as_deref().unwrap_or("dev");
81 let directory = match profile {
82 "dev" | "test" => "debug",
83 "bench" => "release",
84 custom => custom,
85 };
86 let mut components = Path::new(directory).components();
87 if !matches!(
88 (components.next(), components.next()),
89 (Some(Component::Normal(_)), None)
90 ) {
91 return Err("Cargo profile must select one normal output directory component");
92 }
93 Ok(directory)
94 }
95
96 pub(crate) fn metadata_arguments(&self) -> Vec<OsString> {
97 graph_arguments(&self.features, self.locked, self.offline)
98 }
99
100 pub(crate) fn build_arguments(&self) -> Vec<OsString> {
101 let mut arguments = Vec::new();
102 if let Some(profile) = &self.profile {
103 arguments.push(if profile == "release" {
104 "--release".into()
105 } else {
106 format!("--profile={profile}").into()
107 });
108 }
109 arguments.extend(self.metadata_arguments());
110 arguments
111 }
112}
113
114pub(crate) fn graph_arguments(
116 features: &CargoFeatureSelection,
117 locked: bool,
118 offline: bool,
119) -> Vec<OsString> {
120 let mut arguments = Vec::new();
121 for (enabled, flag) in [(locked, "--locked"), (offline, "--offline")] {
122 if enabled {
123 arguments.push(flag.into());
124 }
125 }
126 arguments.extend(features.arguments());
127 arguments
128}