release_kit/projection/
evidence.rs1use std::collections::BTreeMap;
10
11use camino::Utf8Path;
12
13use super::{BLOCK_DESTINATIONS, CrateShape, PythonShape, 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 root_pipeline_present: present(target, super::GITLAB_ROOT_PIPELINE)?,
50 root_pipeline_recorded: recorded
51 .is_some_and(|record| record.file(super::GITLAB_ROOT_PIPELINE).is_some()),
52 python_shape: python_shape(target),
53 })
54}
55
56#[must_use]
59pub fn python_shape(target: &Utf8Path) -> PythonShape {
60 PythonShape {
61 pyproject_toml: std::fs::read_to_string(target.join("pyproject.toml")).ok(),
62 uv_toml: std::fs::read_to_string(target.join("uv.toml")).ok(),
63 uv_lock: target.join("uv.lock").is_file(),
64 }
65}
66
67#[must_use]
70pub fn flake_recorded(recorded: Option<&Manifest>) -> bool {
71 recorded.is_some_and(|record| record.file("flake.nix").is_some())
72}
73
74#[must_use]
77pub fn crate_shape(target: &Utf8Path) -> CrateShape {
78 CrateShape {
79 cargo_toml: std::fs::read_to_string(target.join("Cargo.toml")).ok(),
80 cargo_lock: target.join("Cargo.lock").is_file(),
81 main_rs: target.join("src/main.rs").is_file(),
82 }
83}
84
85pub fn flake_presence(target: &Utf8Path) -> std::io::Result<(bool, bool)> {
93 Ok((
94 present(target, "flake.nix")?,
95 present(target, "flake.lock")?,
96 ))
97}
98
99fn present(target: &Utf8Path, name: &str) -> std::io::Result<bool> {
102 match std::fs::symlink_metadata(target.join(name).as_std_path()) {
103 Ok(_) => Ok(true),
104 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
105 Err(e) => Err(e),
106 }
107}
108
109fn read_optional(path: &Utf8Path) -> std::io::Result<Option<Vec<u8>>> {
111 match std::fs::read(path) {
112 Ok(bytes) => Ok(Some(bytes)),
113 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
114 Err(e) => Err(e),
115 }
116}