Skip to main content

isothrum_compiler/
project.rs

1//! The project's own files, next to `isoloom.yml` where the spec's paths point: an appliance's
2//! configuration (`configs/<machine>.cfg`), and the places the user's own files go (a Dynamips
3//! router's IOS image, `images/<machine>.bin`).
4
5use crate::{Kind, Topology};
6
7/// A file of the project's project, next to `isoloom.yml` where its paths point: an appliance's
8/// configuration, a starter set-up script.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct ProjectFile {
11    /// Its path in the project, relative.
12    pub path: String,
13    /// What it holds.
14    pub contents: String,
15}
16
17/// Where a Dynamips router's IOS image goes in the project (the user's to add).
18pub(crate) fn firmware_path(machine: &str) -> String {
19    format!("images/{machine}.bin")
20}
21
22/// Where an appliance's own configuration goes in the project.
23pub(crate) fn config_path(machine: &str) -> String {
24    format!("configs/{machine}.cfg")
25}
26
27/// The appliances' own configurations, as files of the project, each ending with a line break.
28pub(crate) fn config_files(topo: &Topology) -> Vec<ProjectFile> {
29    topo.nodes
30        .iter()
31        .filter_map(|(name, n)| match &n.kind {
32            Kind::Container {
33                appliance: Some(_),
34                config: Some(c),
35                ..
36            } if !c.trim().is_empty() => Some(ProjectFile {
37                path: config_path(name),
38                contents: if c.ends_with('\n') { c.clone() } else { format!("{c}\n") },
39            }),
40            _ => None,
41        })
42        .collect()
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn a_blank_configuration_is_no_file_and_a_last_line_break_is_added_once() {
51        let topo: Topology = serde_json::from_str(
52            r#"{"name": "x", "nodes": {
53              "r1": {"kind": {"container": {"image": "i", "appliance": "cisco-iol", "config": "  \n"}}},
54              "r2": {"kind": {"container": {"image": "i", "appliance": "cisco-iol", "config": "hostname r2"}}},
55              "r3": {"kind": {"container": {"image": "i", "appliance": "cisco-iol", "config": "hostname r3\n"}}},
56              "pc": {"kind": {"container": {"image": "alpine:3.20"}}}}}"#,
57        )
58        .unwrap();
59        let files: Vec<(String, String)> = config_files(&topo).into_iter().map(|f| (f.path, f.contents)).collect();
60        assert_eq!(
61            files,
62            [
63                ("configs/r2.cfg".to_owned(), "hostname r2\n".to_owned()),
64                ("configs/r3.cfg".to_owned(), "hostname r3\n".to_owned())
65            ]
66        );
67    }
68
69    #[test]
70    fn a_machines_files_are_named_after_it() {
71        assert_eq!(config_path("r1"), "configs/r1.cfg");
72        assert_eq!(firmware_path("r1"), "images/r1.bin");
73    }
74}