staxman-old/src/node/nix.rs

74 lines
2.3 KiB
Rust

use crate::nix::parse::flatten_set;
use anyhow::{anyhow, Result};
use std::collections::HashSet;
pub struct StackComposeInfo {
pub project: String,
pub services: Vec<String>,
}
const PROJECT_KEY: &str = "project\0name";
const SERVICES_PREFIX: &str = "services\0";
pub fn parse_arion_compose(file: &str) -> Result<StackComposeInfo> {
let ast = rnix::Root::parse(file).ok()?;
let expr = ast.expr().ok_or_else(|| anyhow!("invalid nix root"))?;
let flattened = flatten_set(expr)?;
if !flattened.contains_key(PROJECT_KEY) {
return Err(anyhow!("missing required key 'project.name'"));
}
let project = flattened[PROJECT_KEY].clone().trim_matches('"').to_string();
let services = flattened
.iter()
.filter_map(|(key, _)| {
key.strip_prefix(SERVICES_PREFIX)
.and_then(|key| key.split_once('\0'))
.map(|k| k.0.to_string())
})
.collect::<HashSet<_>>()
.into_iter()
.collect();
Ok(StackComposeInfo { project, services })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_compose() {
let source = r#"{
project.name = "traefik";
services.router = {
service.image = "traefik:v3.0";
service.restart = "always";
service.volumes = [
"${toString ./.}/traefik.toml:/traefik.toml"
"${toString ./.}/acme.json:/acme.json"
"${toString ./.}/rules.d:/rules.d"
"/var/run/docker.sock:/var/run/docker.sock:ro"
];
service.ports = [
"80:80"
];
service.labels = {
"traefik.enable" = "true";
"traefik.http.routers.traefik_dashboard.rule" = "Host(`traefik.uchunext.local`)";
"traefik.http.routers.traefik_dashboard.service" = "api@internal";
"traefik.http.routers.traefik_dashboard.entrypoints" = "web";
"traefik.http.services.dashboard.loadbalancer.server.port" = "8080";
};
service.networks = ["web"];
};
networks.web.external = true;
}"#;
let info = parse_arion_compose(source).unwrap();
assert_eq!(info.project, "traefik");
assert_eq!(info.services[0], "router");
}
}