release_kit/projection/
evidence.rs1use std::collections::BTreeMap;
10
11use camino::Utf8Path;
12
13use super::{BLOCK_DESTINATIONS, CrateShape, TargetEvidence};
14use crate::landing::manifest::Manifest;
15
16impl TargetEvidence {
17 pub fn gather(target: &Utf8Path, recorded: Option<&Manifest>) -> std::io::Result<Self> {
24 gather(target, recorded)
25 }
26}
27
28pub fn gather(target: &Utf8Path, recorded: Option<&Manifest>) -> std::io::Result<TargetEvidence> {
36 let mut documents = BTreeMap::new();
37 for destination in BLOCK_DESTINATIONS {
38 if let Some(bytes) = read_optional(&target.join(destination))? {
39 documents.insert(destination.to_owned(), bytes);
40 }
41 }
42 let (flake_nix_present, flake_lock_present) = flake_presence(target)?;
43 Ok(TargetEvidence {
44 documents,
45 crate_shape: crate_shape(target),
46 flake_nix_present,
47 flake_lock_present,
48 flake_recorded: flake_recorded(recorded),
49 })
50}
51
52#[must_use]
55pub fn flake_recorded(recorded: Option<&Manifest>) -> bool {
56 recorded.is_some_and(|record| record.file("flake.nix").is_some())
57}
58
59#[must_use]
62pub fn crate_shape(target: &Utf8Path) -> CrateShape {
63 CrateShape {
64 cargo_toml: std::fs::read_to_string(target.join("Cargo.toml")).ok(),
65 cargo_lock: target.join("Cargo.lock").is_file(),
66 main_rs: target.join("src/main.rs").is_file(),
67 }
68}
69
70pub fn flake_presence(target: &Utf8Path) -> std::io::Result<(bool, bool)> {
78 let present = |name: &str| match std::fs::symlink_metadata(target.join(name).as_std_path()) {
79 Ok(_) => Ok(true),
80 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
81 Err(e) => Err(e),
82 };
83 Ok((present("flake.nix")?, present("flake.lock")?))
84}
85
86fn read_optional(path: &Utf8Path) -> std::io::Result<Option<Vec<u8>>> {
88 match std::fs::read(path) {
89 Ok(bytes) => Ok(Some(bytes)),
90 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
91 Err(e) => Err(e),
92 }
93}