gh_workflow/
toolchain.rs

1//! The typed version of the `setup-rust-toolchain` action in a GitHub workflow.
2//! Docs: <https://github.com/actions-rust-lang/setup-rust-toolchain>
3
4use std::fmt::{Display, Formatter};
5
6use derive_setters::Setters;
7
8use crate::{private, Input, RustFlags, Step, Use};
9
10#[derive(Clone)]
11pub enum Version {
12    Stable,
13    Nightly,
14    Custom((u64, u64, u64)),
15}
16
17impl Display for Version {
18    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
19        match self {
20            Version::Stable => write!(f, "stable"),
21            Version::Nightly => write!(f, "nightly"),
22            Version::Custom(s) => write!(f, "{}.{}.{}", s.0, s.1, s.2),
23        }
24    }
25}
26
27impl Version {
28    pub fn new(major: u64, minor: u64, patch: u64) -> Self {
29        Version::Custom((major, minor, patch))
30    }
31}
32
33#[derive(Clone, Debug)]
34pub enum Component {
35    Clippy,
36    Rustfmt,
37    RustDoc,
38}
39
40impl Display for Component {
41    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
42        let val = match self {
43            Component::Clippy => "clippy",
44            Component::Rustfmt => "rustfmt",
45            Component::RustDoc => "rust-doc",
46        };
47        write!(f, "{val}")
48    }
49}
50
51#[derive(Clone)]
52pub enum Arch {
53    X86_64,
54    Aarch64,
55    Arm,
56    Wasm32,
57}
58
59impl Display for Arch {
60    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
61        let val = match self {
62            Arch::X86_64 => "x86_64",
63            Arch::Aarch64 => "aarch64",
64            Arch::Arm => "arm",
65            Arch::Wasm32 => "wasm32",
66        };
67        write!(f, "{val}")
68    }
69}
70
71#[derive(Clone)]
72pub enum Vendor {
73    Unknown,
74    Apple,
75    PC,
76}
77
78impl Display for Vendor {
79    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
80        let val = match self {
81            Vendor::Unknown => "unknown",
82            Vendor::Apple => "apple",
83            Vendor::PC => "pc",
84        };
85        write!(f, "{val}")
86    }
87}
88
89#[derive(Clone)]
90pub enum System {
91    Unknown,
92    Windows,
93    Linux,
94    Darwin,
95}
96
97impl Display for System {
98    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
99        let val = match self {
100            System::Unknown => "unknown",
101            System::Windows => "windows",
102            System::Linux => "linux",
103            System::Darwin => "darwin",
104        };
105        write!(f, "{val}")
106    }
107}
108
109#[derive(Clone)]
110pub enum Abi {
111    Unknown,
112    Gnu,
113    Msvc,
114    Musl,
115}
116
117impl Display for Abi {
118    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
119        let val = match self {
120            Abi::Unknown => "unknown",
121            Abi::Gnu => "gnu",
122            Abi::Msvc => "msvc",
123            Abi::Musl => "musl",
124        };
125        write!(f, "{val}")
126    }
127}
128
129#[derive(Clone, Setters)]
130pub struct Target {
131    pub arch: Arch,
132    pub vendor: Vendor,
133    pub system: System,
134    pub abi: Option<Abi>,
135}
136
137///
138/// A Rust representation for the inputs of the setup-rust action.
139/// More information can be found [here](https://github.com/actions-rust-lang/setup-rust-toolchain/blob/main/action.yml).
140/// NOTE: The public API should be close to the original action as much as
141/// possible.
142#[derive(Default, Clone, Setters)]
143#[setters(
144    strip_option,
145    into,
146    generate_delegates(ty = "Step<Toolchain>", field = "marker")
147)]
148pub struct Toolchain {
149    pub version: Vec<Version>,
150    #[setters(skip)]
151    pub target: Option<Target>,
152    pub components: Vec<Component>,
153    pub cache: Option<bool>,
154    pub cache_directories: Vec<String>,
155    pub cache_workspaces: Vec<String>,
156    pub cache_on_failure: Option<bool>,
157    pub cache_key: Option<String>,
158    pub matcher: Option<bool>,
159    pub rust_flags: Option<RustFlags>,
160    pub override_default: Option<bool>,
161}
162
163impl Toolchain {
164    pub fn add_version(mut self, version: Version) -> Self {
165        self.version.push(version);
166        self
167    }
168
169    pub fn add_component(mut self, component: Component) -> Self {
170        self.components.push(component);
171        self
172    }
173
174    pub fn add_stable(mut self) -> Self {
175        self.version.push(Version::Stable);
176        self
177    }
178
179    pub fn add_nightly(mut self) -> Self {
180        self.version.push(Version::Nightly);
181        self
182    }
183
184    pub fn add_clippy(mut self) -> Self {
185        self.components.push(Component::Clippy);
186        self
187    }
188
189    pub fn add_fmt(mut self) -> Self {
190        self.components.push(Component::Rustfmt);
191        self
192    }
193
194    pub fn target(mut self, arch: Arch, vendor: Vendor, system: System, abi: Option<Abi>) -> Self {
195        self.target = Some(Target { arch, vendor, system, abi });
196        self
197    }
198}
199
200impl From<Toolchain> for Step<Use> {
201    fn from(value: Toolchain) -> Self {
202        let mut step = Step::new("Setup Rust Toolchain").uses(
203            "actions-rust-lang",
204            "setup-rust-toolchain",
205            "v1",
206        );
207
208        let toolchain = value
209            .version
210            .iter()
211            .map(|t| match t {
212                Version::Stable => "stable".to_string(),
213                Version::Nightly => "nightly".to_string(),
214                Version::Custom((major, minor, patch)) => {
215                    format!("{major}.{minor}.{patch}")
216                }
217            })
218            .reduce(|acc, a| format!("{acc}, {a}"));
219
220        let mut input = Input::default();
221
222        if let Some(toolchain) = toolchain {
223            input = input.add("toolchain", toolchain);
224        }
225
226        if let Some(target) = value.target {
227            let target = format!(
228                "{}-{}-{}{}",
229                target.arch,
230                target.vendor,
231                target.system,
232                target.abi.map(|v| v.to_string()).unwrap_or_default(),
233            );
234
235            input = input.add("target", target);
236        }
237
238        if !value.components.is_empty() {
239            let components = value
240                .components
241                .iter()
242                .map(|c| c.to_string())
243                .reduce(|acc, a| format!("{acc}, {a}"))
244                .unwrap_or_default();
245
246            input = input.add("components", components);
247        }
248
249        if let Some(cache) = value.cache {
250            input = input.add("cache", cache);
251        }
252
253        if !value.cache_directories.is_empty() {
254            let cache_directories = value.cache_directories.join("\n");
255            input = input.add("cache-directories", cache_directories);
256        }
257
258        if !value.cache_workspaces.is_empty() {
259            let cache_workspaces = value
260                .cache_workspaces
261                .iter()
262                .fold("".to_string(), |acc, a| format!("{acc}\n{a}"));
263
264            input = input.add("cache-workspaces", cache_workspaces);
265        }
266
267        if let Some(cache_on_failure) = value.cache_on_failure {
268            input = input.add("cache-on-failure", cache_on_failure);
269        }
270
271        if let Some(cache_key) = value.cache_key {
272            input = input.add("cache-key", cache_key);
273        }
274
275        if let Some(matcher) = value.matcher {
276            input = input.add("matcher", matcher);
277        }
278
279        if let Some(rust_flags) = value.rust_flags {
280            input = input.add("rust-flags", rust_flags.to_string());
281        }
282
283        if let Some(override_default) = value.override_default {
284            input = input.add("override", override_default);
285        }
286
287        if !input.is_empty() {
288            step = step.with(input);
289        }
290        step
291    }
292}
293
294impl private::Sealed for Toolchain {}