1use 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 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 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}