isothrum_compiler/lib.rs
1//! Isothrum's compiler: a lab as drawn in the editor (a [`Topology`]: machines, the LANs drawn
2//! around them, and the cables between their ports) becomes an Isoloom spec, in the format Isoloom
3//! reads (`isoloom.yml`, version 1).
4//!
5//! - **Machines.** A container node is a custom app from its image (`app.image`), kept running
6//! idle with its own `command` when the image runs nothing of its own; a Cisco node is an
7//! appliance (`app.appliance`). A VM node is its OS (`os`), set up by its provisioning steps
8//! (`app.steps`), or the OS alone: each target decides how to deliver it.
9//! - **Ports** are named (`ssh`, `http`, `port-8000`, `dns-udp`), each with its `protocol`.
10//! - **Networks.** A LAN (an area drawn on the canvas, a VLAN in practice) is an Isoloom network
11//! with its own block; each machine on it gets an address there, the next free one unless it
12//! asks for one. A cable between two machines is its own point-to-point network (a /29 cut from
13//! `10.200.0.0/16`, the two ends on its first two usable addresses). A switch's VLANs nest
14//! under a network named after the switch.
15//! - **Routing.** An automatic router lets the networks it joins reach each other (`reach`, both
16//! ways); a Cisco router is its LANs' gateway.
17//!
18//! Every compiled spec is read back by Isoloom's parser before it is returned, so what the
19//! compiler hands out is a spec Isoloom reads. Whether the lab is valid as a whole (an unattached
20//! machine, overlapping blocks) is Isoloom's validation to say.
21//!
22//! A Windows machine's own steps are PowerShell scripts: the Ansible playbooks drawn on one run
23//! from Isoloom's controller (`provision:`), as Isoloom's own Windows examples do.
24//!
25//! The lab's own files come with the spec ([`Compiled::files`], as [`LabFile`]s): the appliances'
26//! configurations, and a starter for each provisioning step to edit, so a downloaded lab runs as
27//! is.
28//!
29//! The modules follow [`compile`]: the drawing as read (`topology`); its `machines` and their
30//! `ports`; the playbooks Isoloom's `controller` runs; the `lans` and their addresses, the
31//! `cables`, the `routers` and the switches' `vlans`; the spec as it is built (`draft`) and
32//! written (`document`). `project` and `starters` are the lab's own files; `error` is what a
33//! drawing can get wrong.
34//!
35//! ```
36//! let topology: isothrum_compiler::Topology = serde_json::from_str(
37//! r#"{
38//! "name": "pair",
39//! "networks": { "office": { "cidr": "10.10.1.0/24" } },
40//! "nodes": {
41//! "web": { "kind": { "container": { "image": "nginx:1.27-alpine" } },
42//! "services": [{ "port": 80, "protocol": "http" }], "networks": { "office": null } },
43//! "user": { "kind": { "vm": { "os": "debian@12" } }, "access": true, "networks": { "office": null } }
44//! }
45//! }"#,
46//! )?;
47//! let compiled = isothrum_compiler::compile(&topology)?;
48//! assert!(compiled.yaml().contains("app:\n image: nginx:1.27-alpine"));
49//! assert!(compiled.yaml().contains("ports:\n http:\n port: 80\n protocol: http"));
50//! assert_eq!(compiled.spec().machines["user"].os.map(|os| os.to_string()).as_deref(), Some("debian@12"));
51//! # Ok::<(), Box<dyn std::error::Error>>(())
52//! ```
53
54mod cables;
55mod compiled;
56mod controller;
57mod document;
58mod draft;
59mod error;
60mod lans;
61mod machines;
62mod ports;
63mod project;
64mod routers;
65mod starters;
66mod topology;
67mod vlans;
68
69pub use compiled::{Compiled, compile};
70pub use error::{Error, Result};
71pub use project::LabFile;
72pub use topology::{Endpoint, Kind, Lan, Link, Node, Service, Topology};