Rust Crates (Ecosystem)
Overview
Crates are Rust’s packages. Find them at crates.io.
Add to Cargo.toml:
[dependencies]
crate_name = "version"
handlebars - Templating
Same {{variable}} syntax as Antora UI templates.
[dependencies]
handlebars = "6"
serde_json = "1"
use handlebars::Handlebars;
use serde_json::json;
fn main() {
let mut hbs = Handlebars::new();
// Register template
hbs.register_template_string("host", "Server: {{hostname}} at {{ip}}")
.unwrap();
// Render with data
let data = json!({"hostname": "kvm-01", "ip": "10.50.1.100"});
let output = hbs.render("host", &data).unwrap();
println!("{}", output);
// Output: Server: kvm-01 at 10.50.1.100
}
use handlebars::Handlebars;
use serde_json::json;
fn main() {
let mut hbs = Handlebars::new();
// Load template file
hbs.register_template_file("report", "templates/report.hbs")
.unwrap();
let data = json!({
"title": "Network Report",
"hosts": ["kvm-01", "kvm-02", "kvm-03"]
});
println!("{}", hbs.render("report", &data).unwrap());
}
# {{title}}
{{#each hosts}}
- {{this}}
{{/each}}
{{#if active}}
Status: ONLINE
{{else}}
Status: OFFLINE
{{/if}}
serde - Serialization
The standard for JSON, YAML, TOML parsing.
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct NetworkDevice {
hostname: String,
ip: String,
vlan: u16,
}
fn main() {
// Struct to JSON
let device = NetworkDevice {
hostname: "sw-01".to_string(),
ip: "10.50.1.10".to_string(),
vlan: 10,
};
let json = serde_json::to_string_pretty(&device).unwrap();
println!("{}", json);
// JSON to Struct
let json_str = r#"{"hostname":"kvm-01","ip":"10.50.1.100","vlan":50}"#;
let parsed: NetworkDevice = serde_json::from_str(json_str).unwrap();
println!("{:?}", parsed);
}
clap - CLI Argument Parsing
Build command-line tools like ripgrep, fd.
[dependencies]
clap = { version = "4", features = ["derive"] }
use clap::Parser;
#[derive(Parser)]
#[command(name = "netcheck")]
#[command(about = "Check network connectivity")]
struct Cli {
/// Target IP or hostname
target: String,
/// Port to check
#[arg(short, long, default_value_t = 443)]
port: u16,
/// Verbose output
#[arg(short, long)]
verbose: bool,
}
fn main() {
let cli = Cli::parse();
if cli.verbose {
println!("Checking {}:{}", cli.target, cli.port);
}
// ... do the check
}
netcheck 10.50.1.50 --port 22 --verbose
netcheck kvm-01 -p 443 -v
netcheck --help
reqwest - HTTP Client
Make HTTP requests (like curl).
[dependencies]
reqwest = { version = "0.12", features = ["blocking", "json"] }
serde = { version = "1", features = ["derive"] }
fn main() -> Result<(), Box<dyn std::error::Error>> {
let body = reqwest::blocking::get("https://httpbin.org/ip")?
.text()?;
println!("{}", body);
Ok(())
}
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
struct Payload {
hostname: String,
action: String,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::blocking::Client::new();
let payload = Payload {
hostname: "kvm-01".to_string(),
action: "reboot".to_string(),
};
let resp = client
.post("https://api.example.com/hosts")
.json(&payload)
.send()?;
println!("Status: {}", resp.status());
Ok(())
}
tokio - Async Runtime
Required for async Rust (non-blocking I/O).
[dependencies]
tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() {
let result = do_something_async().await;
println!("{}", result);
}
async fn do_something_async() -> String {
// async operations here
"done".to_string()
}
Infrastructure Documentation with cargo doc
Use Rust types to document your infrastructure. The docs become executable, testable reference.
D2 vs cargo doc:
-
D2 = Visual diagram: "kvm-01 connects to sw-01 on VLAN 50"
-
cargo doc = Type reference: "NetworkDevice has hostname, ip, vlan fields with these constraints"
Use BOTH: D2 for topology, cargo doc for API/type reference.
//! # Domus Infrastructure Types
//!
//! Documented infrastructure for the home enterprise.
//!
//! ## Network Layout
//!
//! | VLAN | Purpose | Subnet |
//! |------|---------|--------|
//! | 10 | Data | 10.50.10.0/24 |
//! | 50 | Management | 10.50.1.0/24 |
//! | 100 | Guest | 10.50.100.0/24 |
use serde::{Deserialize, Serialize};
/// A hypervisor running KVM/libvirt.
///
/// # Infrastructure Role
///
/// Hypervisors host all virtual machines in the environment.
/// Each hypervisor connects to management VLAN 50.
///
/// # Examples
///
/// ```
/// use infra::Hypervisor;
///
/// let kvm01 = Hypervisor {
/// hostname: "kvm-01".to_string(),
/// ip: "10.50.1.100".to_string(),
/// cores: 32,
/// ram_gb: 128,
/// storage_tb: 4,
/// };
///
/// assert_eq!(kvm01.fqdn(), "kvm-01.inside.domusdigitalis.dev");
/// ```
#[derive(Debug, Serialize, Deserialize)]
pub struct Hypervisor {
/// Short hostname (e.g., "kvm-01")
pub hostname: String,
/// Management IP on VLAN 50
pub ip: String,
/// Physical CPU cores
pub cores: u8,
/// RAM in gigabytes
pub ram_gb: u16,
/// Storage in terabytes
pub storage_tb: u8,
}
impl Hypervisor {
/// Returns the fully qualified domain name.
pub fn fqdn(&self) -> String {
format!("{}.inside.domusdigitalis.dev", self.hostname)
}
}
/// Virtual machine running on a hypervisor.
///
/// # Lifecycle
///
/// VMs are provisioned via:
/// 1. `virt-install` for initial creation
/// 2. Ansible for configuration
/// 3. FreeIPA for identity
///
/// # Examples
///
/// ```
/// use infra::{VirtualMachine, VmState};
///
/// let ipa01 = VirtualMachine {
/// hostname: "ipa-01".to_string(),
/// ip: "10.50.1.51".to_string(),
/// vcpus: 4,
/// ram_gb: 8,
/// hypervisor: "kvm-01".to_string(),
/// state: VmState::Running,
/// };
///
/// assert!(ipa01.is_running());
/// ```
#[derive(Debug, Serialize, Deserialize)]
pub struct VirtualMachine {
pub hostname: String,
pub ip: String,
pub vcpus: u8,
pub ram_gb: u16,
pub hypervisor: String,
pub state: VmState,
}
impl VirtualMachine {
pub fn is_running(&self) -> bool {
matches!(self.state, VmState::Running)
}
}
/// VM power state.
#[derive(Debug, Serialize, Deserialize)]
pub enum VmState {
/// VM is running
Running,
/// VM is stopped
Stopped,
/// VM is paused (suspended)
Paused,
}
/// Network VLAN definition.
///
/// # Examples
///
/// ```
/// use infra::Vlan;
///
/// let mgmt = Vlan::new(50, "Management", "10.50.1.0/24");
/// assert_eq!(mgmt.gateway(), "10.50.1.1");
/// ```
#[derive(Debug)]
pub struct Vlan {
pub id: u16,
pub name: String,
pub subnet: String,
}
impl Vlan {
pub fn new(id: u16, name: &str, subnet: &str) -> Self {
Self {
id,
name: name.to_string(),
subnet: subnet.to_string(),
}
}
/// Returns the gateway (first usable IP).
pub fn gateway(&self) -> String {
// Simplified: assumes .1 is gateway
let parts: Vec<&str> = self.subnet.split('/').collect();
let octets: Vec<&str> = parts[0].split('.').collect();
format!("{}.{}.{}.1", octets[0], octets[1], octets[2])
}
}
/// Cloud provider resources.
pub mod cloud {
//! # Cloud Resources
//!
//! Types for documenting cloud infrastructure.
/// Cloudflare Pages deployment.
///
/// # Examples
///
/// ```
/// use infra::cloud::CloudflarePages;
///
/// let docs = CloudflarePages {
/// project: "domus-docs".to_string(),
/// domain: "docs.domusdigitalis.dev".to_string(),
/// build_cmd: "npx antora antora-playbook.yml".to_string(),
/// output_dir: "build/site".to_string(),
/// };
/// ```
#[derive(Debug)]
pub struct CloudflarePages {
pub project: String,
pub domain: String,
pub build_cmd: String,
pub output_dir: String,
}
/// AWS EC2 instance type reference.
///
/// # Size Reference
///
/// | Type | vCPUs | RAM | Use Case |
/// |------|-------|-----|----------|
/// | t3.micro | 2 | 1GB | Testing |
/// | t3.small | 2 | 2GB | Dev |
/// | t3.medium | 2 | 4GB | Small prod |
/// | m5.large | 2 | 8GB | Production |
#[derive(Debug)]
pub struct Ec2Instance {
pub instance_id: String,
pub instance_type: String,
pub availability_zone: String,
pub private_ip: String,
pub state: String,
}
}
cargo doc --open
You get:
-
Searchable HTML docs for all your infrastructure types
-
Tables render in the docs (VLAN table, EC2 sizes)
-
Examples that are TESTED (
cargo test --doc) -
Cross-links between types (
[`VmState]` links to enum)
When to use what:
| Need | Tool | |------|------| | Show network topology | D2 | | Show data flow | D2 | | Document types/APIs | cargo doc | | Executable examples | cargo doc | | Architecture overview | D2 | | Configuration reference | cargo doc |
Crates to Explore Later
| Crate | Purpose | When |
|-------|---------|------|
| anyhow | Simplified error handling | After Error Handling section |
| thiserror | Custom error types | After Error Handling section |
| regex | Regular expressions | When parsing text |
| chrono | Date/time | When working with timestamps |
| log + env_logger | Logging | For CLI tools |
| ssh2 | SSH connections | For infrastructure tools |
| rusqlite | SQLite database | For local storage |