use serde::Serialize;
use crate::cli::payload::PayloadArgs;
use crate::digest::Digest;
use crate::embedded;
use crate::error::RkError;
use crate::output::Output;
const PAYLOAD_SCHEMA: u32 = 1;
#[derive(Debug, Serialize)]
pub struct Artifact {
pub path: String,
pub sha256: Digest,
}
#[derive(Debug, Serialize)]
pub struct Report {
pub release_kit_version: &'static str,
pub payload_schema: u32,
pub payload_sha256: Digest,
pub artifacts: Vec<Artifact>,
}
#[must_use]
pub fn report() -> Report {
let artifacts: Vec<Artifact> = embedded::artifacts()
.into_iter()
.map(|(path, bytes)| Artifact {
path,
sha256: Digest::of(bytes),
})
.collect();
Report {
release_kit_version: env!("CARGO_PKG_VERSION"),
payload_schema: PAYLOAD_SCHEMA,
payload_sha256: aggregate(&artifacts),
artifacts,
}
}
fn aggregate(artifacts: &[Artifact]) -> Digest {
let mut lines = String::new();
for artifact in artifacts {
lines.push_str(&artifact.path);
lines.push('\n');
lines.push_str(&artifact.sha256.to_string());
lines.push('\n');
}
Digest::of(lines.as_bytes())
}
pub fn run(args: &PayloadArgs) -> Result<(), RkError> {
let out = Output::new(args.json);
let report = report();
out.result_line(format!("release-kit {}", report.release_kit_version));
out.result_line(format!("payload sha256 {}", report.payload_sha256));
for root in embedded::PAYLOAD_ROOTS {
let count = report
.artifacts
.iter()
.filter(|a| a.path == root || a.path.starts_with(&format!("{root}/")))
.count();
let noun = if count == 1 { "file" } else { "files" };
out.result_line(format!("{root}: {count} {noun}"));
}
out.emit(&report)
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)]
use super::{Artifact, Report, aggregate, report};
use crate::digest::Digest;
#[test]
fn the_payload_report_schema_snapshot_holds() {
let fixture = Report {
release_kit_version: "0.0.0",
payload_schema: 1,
payload_sha256: Digest::of(b""),
artifacts: vec![Artifact {
path: "versions.toml".into(),
sha256: Digest::of(b""),
}],
};
assert_eq!(
serde_json::to_string(&fixture).expect("a report serializes"),
r#"{"release_kit_version":"0.0.0","payload_schema":1,"payload_sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","artifacts":[{"path":"versions.toml","sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}]}"#
);
}
#[test]
fn the_report_names_the_cargo_version_and_every_artifact() {
let report = report();
assert_eq!(report.release_kit_version, env!("CARGO_PKG_VERSION"));
assert!(!report.artifacts.is_empty());
assert_eq!(report.payload_sha256, aggregate(&report.artifacts));
}
#[test]
fn the_aggregate_covers_paths_and_order() {
let mut artifacts = report().artifacts;
let original = aggregate(&artifacts);
artifacts.reverse();
assert_ne!(original, aggregate(&artifacts));
}
}