this repo has no description
1use color_eyre::Result;
2use serde::de::Deserializer;
3use serde::Deserialize;
4use std::{
5 borrow::Cow,
6 collections::BTreeMap,
7 path::{Path, PathBuf},
8 process::Command,
9};
10
11#[derive(Deserialize, Debug)]
12pub struct DiffRoot {
13 pub packages: BTreeMap<String, DiffPackage>,
14
15 #[expect(dead_code)]
16 pub schema: String,
17}
18
19#[derive(Deserialize, Debug)]
20#[serde(rename_all = "camelCase")]
21pub struct DiffPackage {
22 pub size_delta: i64,
23
24 #[serde(deserialize_with = "version_deserializer")]
25 pub versions_before: Vec<String>,
26
27 #[serde(deserialize_with = "version_deserializer")]
28 pub versions_after: Vec<String>,
29}
30
31fn version_deserializer<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
32where
33 D: Deserializer<'de>,
34{
35 let vec = Vec::<Cow<'de, str>>::deserialize(deserializer)?;
36 Ok(vec
37 .into_iter()
38 .map(|s| {
39 if s.is_empty() {
40 "<none>".to_string()
41 } else {
42 s.into_owned()
43 }
44 })
45 .collect())
46}
47
48impl DiffRoot {
49 pub fn new(lix_path: Option<PathBuf>, before: &Path, after: &Path) -> Result<DiffRoot> {
50 let lix_exe;
51 if let Some(lix_path) = lix_path {
52 lix_exe = lix_path;
53 } else {
54 lix_exe = "nix".into();
55 }
56
57 let raw_diff = Command::new(lix_exe)
58 .args(["store", "diff-closures", "--json"])
59 .args([before, after])
60 .output()?;
61
62 if !raw_diff.status.success() {
63 eprintln!("{}", String::from_utf8_lossy(&raw_diff.stderr));
64 std::process::exit(1);
65 }
66
67 let stdout = raw_diff.stdout;
68 if stdout.is_empty() {
69 eprintln!("No differences found.");
70 std::process::exit(0);
71 }
72
73 // Assume nix output is valid UTF-8
74 let diff_out = String::from_utf8(stdout)?;
75
76 let diff_root = serde_json::from_str::<DiffRoot>(&diff_out).map_err(|e| {
77 eprintln!("Failed to parse JSON: {e}");
78 std::process::exit(1);
79 })?;
80
81 Ok(diff_root)
82 }
83}