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