isoloom_spec/model/configuration.rs
1//! What configures a machine, as lowering hands it to the plan: its steps in the configuration
2//! model's three phases, for its OS, with the texts of the templates they fill in. The plan
3//! resolves it once per machine into its recipe (`isoloom_plan::configure::Recipe`), which every
4//! CM renderer reads; nothing below the plan renders it.
5
6use std::collections::BTreeMap;
7
8use crate::syntax::{OsRef, ProjectPath, Step};
9
10/// A machine's configuration: an app's `steps` (all of them `install`), or a catalogue entry's
11/// phases, resolved from what the author wrote. Set by lowering, never written in a spec.
12#[derive(Debug, Clone, PartialEq, Eq)]
13#[non_exhaustive]
14pub struct Configuration {
15 /// The OS it is resolved for: its family installs and starts things.
16 pub os: OsRef,
17 /// Install: an app's steps; an entry's `install` (which its official image skips).
18 pub install: Vec<Step>,
19 /// Configure: an entry's `configure`, run in every form.
20 pub configure: Vec<Step>,
21 /// Start: an entry's per-start preparation (an app has none in 1.0).
22 pub start: Vec<Step>,
23 /// The text of each template its steps fill in, by the template's `from`: an entry's own
24 /// (its placeholders filled in, the lab's left for the plan), or a project file read by the
25 /// caller that generates (`isoloom_plan::Lab::with_templates`).
26 pub files: BTreeMap<ProjectPath, String>,
27}
28
29impl Configuration {
30 /// An app's configuration on `os`: its steps, all of them installing.
31 #[must_use]
32 pub fn app(os: OsRef, steps: Vec<Step>) -> Configuration {
33 Configuration {
34 os,
35 install: steps,
36 configure: Vec::new(),
37 start: Vec::new(),
38 files: BTreeMap::new(),
39 }
40 }
41
42 /// A catalogue entry's configuration on `os`: its three phases, resolved, and the texts of
43 /// its templates by the `from` its steps name.
44 #[must_use]
45 pub fn entry(os: OsRef, install: Vec<Step>, configure: Vec<Step>, start: Vec<Step>, files: BTreeMap<ProjectPath, String>) -> Configuration {
46 Configuration {
47 os,
48 install,
49 configure,
50 start,
51 files,
52 }
53 }
54
55 /// Its steps, phase after phase.
56 pub fn steps(&self) -> impl Iterator<Item = &Step> {
57 self.install.iter().chain(&self.configure).chain(&self.start)
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn an_apps_steps_all_install_and_read_back_in_order() {
67 let os: OsRef = "debian@12".parse().unwrap();
68 let steps: Vec<Step> = serde_yaml_ng::from_str("[{ run: a.sh }, reboot]").unwrap();
69 let c = Configuration::app(os, steps.clone());
70 assert_eq!(c.install, steps);
71 assert!(c.configure.is_empty() && c.start.is_empty() && c.files.is_empty());
72 assert_eq!(c.steps().map(Step::kind).collect::<Vec<_>>(), ["run", "reboot"]);
73 }
74}