Nix flake configuration manager
1use std::process::Command;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum ConfigKind {
5 NixOS,
6 HomeManager,
7 Colmena,
8}
9
10impl ConfigKind {
11 pub fn label(&self) -> &'static str {
12 match self {
13 ConfigKind::NixOS => "NixOS",
14 ConfigKind::HomeManager => "Home Manager",
15 ConfigKind::Colmena => "Colmena",
16 }
17 }
18}
19
20#[derive(Debug, Clone)]
21pub struct ConfigEntry {
22 pub name: String,
23 pub kind: ConfigKind,
24}
25
26/// Discover all NixOS, Home Manager, and Colmena configurations in the flake.
27pub fn discover(flake_path: &str) -> Vec<ConfigEntry> {
28 let mut entries = Vec::new();
29 entries.extend(discover_attr(flake_path, "nixosConfigurations", ConfigKind::NixOS));
30 entries.extend(discover_attr(flake_path, "homeConfigurations", ConfigKind::HomeManager));
31 entries.extend(discover_colmena(flake_path));
32 entries
33}
34
35fn nix_attr_names(flake_path: &str, attr: &str) -> Vec<String> {
36 let flake_ref = format!("{}#{}", flake_path, attr);
37 let output = Command::new("nix")
38 .args([
39 "eval",
40 "--json",
41 &flake_ref,
42 "--apply",
43 "builtins.attrNames",
44 ])
45 .output();
46
47 match output {
48 Ok(out) if out.status.success() => {
49 let json = String::from_utf8_lossy(&out.stdout);
50 serde_json::from_str::<Vec<String>>(&json).unwrap_or_default()
51 }
52 _ => vec![],
53 }
54}
55
56fn discover_attr(flake_path: &str, attr: &str, kind: ConfigKind) -> Vec<ConfigEntry> {
57 nix_attr_names(flake_path, attr)
58 .into_iter()
59 .map(|name| ConfigEntry { name, kind: kind.clone() })
60 .collect()
61}
62
63fn discover_colmena(flake_path: &str) -> Vec<ConfigEntry> {
64 // Colmena uses `colmena` attribute; `meta` is config, not a host
65 let flake_ref = format!("{}#colmena", flake_path);
66 let output = Command::new("nix")
67 .args([
68 "eval",
69 "--json",
70 &flake_ref,
71 "--apply",
72 r#"x: builtins.attrNames (builtins.removeAttrs x ["meta"])"#,
73 ])
74 .output();
75
76 match output {
77 Ok(out) if out.status.success() => {
78 let json = String::from_utf8_lossy(&out.stdout);
79 serde_json::from_str::<Vec<String>>(&json)
80 .unwrap_or_default()
81 .into_iter()
82 .map(|name| ConfigEntry { name, kind: ConfigKind::Colmena })
83 .collect()
84 }
85 _ => vec![],
86 }
87}