Skip to main content

caixa_flake/
render.rs

1//! Render `FlakeLisp` → `flake.nix` source. Transpile mode (always
2//! available); direct-eval mode lands when sui's nixpkgs parity is closer.
3
4use crate::flake::{FlakeLisp, FlakeOutput, FlakePackage};
5
6#[must_use]
7pub fn render_flake_nix(f: &FlakeLisp) -> String {
8    let mut out = String::new();
9    out.push_str("# Auto-generated from flake.lisp. Re-run `feira nix` after edits.\n\n");
10    out.push_str("{\n");
11    out.push_str(&format!("  description = {:?};\n\n", f.descricao));
12
13    // Inputs.
14    out.push_str("  inputs = {\n");
15    for entrada in &f.entradas {
16        if let Some(seg) = &entrada.segue {
17            out.push_str(&format!("    {}.follows = {seg:?};\n", entrada.nome));
18        } else {
19            out.push_str(&format!("    {}.url = {:?};\n", entrada.nome, entrada.url));
20        }
21    }
22    out.push_str("  };\n\n");
23
24    // Outputs.
25    out.push_str("  outputs = { self, ");
26    let names: Vec<&str> = f.entradas.iter().map(|e| e.nome.as_str()).collect();
27    out.push_str(&names.join(", "));
28    out.push_str(", ... }: let\n");
29    out.push_str("    systems = [ \"aarch64-darwin\" \"x86_64-darwin\" \"aarch64-linux\" \"x86_64-linux\" ];\n");
30    out.push_str("    forAllSystems = f: builtins.listToAttrs (map (s: { name = s; value = f s; }) systems);\n");
31    out.push_str("  in {\n");
32
33    if let Some(s) = &f.saidas {
34        render_outputs(s, &mut out);
35    }
36    out.push_str("  };\n}\n");
37    out
38}
39
40fn render_outputs(o: &FlakeOutput, out: &mut String) {
41    if !o.pacotes.is_empty() {
42        out.push_str("    packages = forAllSystems (system:\n");
43        out.push_str("      let pkgs = import nixpkgs { inherit system; }; in {\n");
44        for p in &o.pacotes {
45            render_package(p, out);
46        }
47        out.push_str("      });\n\n");
48    }
49    if o.dev_shells {
50        out.push_str("    devShells = forAllSystems (system:\n");
51        out.push_str("      let pkgs = import nixpkgs { inherit system; }; in {\n");
52        out.push_str("        default = pkgs.mkShell { packages = [ ]; };\n");
53        out.push_str("      });\n");
54    }
55}
56
57fn render_package(p: &FlakePackage, out: &mut String) {
58    out.push_str(&format!(
59        "        {} = pkgs.stdenvNoCC.mkDerivation {{\n\
60         \tpname = {:?};\n\
61         \tversion = \"0.1.0\";\n\
62         \tsrc = {};\n\
63         \tinstallPhase = \"mkdir -p $out && cp -r . $out/\";\n\
64         }};\n",
65        p.nome,
66        p.nome,
67        nix_src(&p.src)
68    ));
69}
70
71fn nix_src(s: &str) -> String {
72    if s.starts_with('.') || s.starts_with('/') {
73        s.to_string()
74    } else {
75        format!("{s:?}")
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::flake::{FlakeInput, FlakeLisp, FlakeOutput, FlakePackage};
83
84    #[test]
85    fn renders_with_inputs_and_package() {
86        let f = FlakeLisp {
87            descricao: "demo".into(),
88            entradas: vec![FlakeInput {
89                nome: "nixpkgs".into(),
90                url: "github:nixos/nixpkgs".into(),
91                segue: None,
92            }],
93            saidas: Some(FlakeOutput {
94                pacotes: vec![FlakePackage {
95                    nome: "default".into(),
96                    src: ".".into(),
97                }],
98                modulos: vec![],
99                dev_shells: true,
100            }),
101        };
102        let out = render_flake_nix(&f);
103        assert!(out.contains("description = \"demo\""));
104        assert!(out.contains("nixpkgs.url = \"github:nixos/nixpkgs\""));
105        assert!(out.contains("packages = forAllSystems"));
106        assert!(out.contains("devShells = forAllSystems"));
107    }
108
109    #[test]
110    fn lisp_round_trip_and_render() {
111        let src = r#"
112(defflake
113  :descricao "demo"
114  :entradas ((:nome "nixpkgs" :url "github:nixos/nixpkgs")))
115"#;
116        let f = FlakeLisp::from_lisp(src).unwrap();
117        let nix = render_flake_nix(&f);
118        assert!(nix.contains("description = \"demo\""));
119    }
120}