140 lines
4.1 KiB
Rust
140 lines
4.1 KiB
Rust
use std::collections::BTreeMap;
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::Value;
|
|
|
|
use crate::query_db::QueryDbResult;
|
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ArtifactFileSummary {
|
|
pub path: String,
|
|
pub size_bytes: Option<u64>,
|
|
pub modified_unix_secs: Option<u64>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct QueryAuditArtifactSummary {
|
|
pub status: Option<String>,
|
|
pub events_path: Option<String>,
|
|
pub events_count: Option<u64>,
|
|
pub events_sha256: Option<String>,
|
|
pub writer_version: Option<u64>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ArtifactManifestSummary {
|
|
pub files: BTreeMap<String, ArtifactFileSummary>,
|
|
pub query_audit: Option<QueryAuditArtifactSummary>,
|
|
pub cir_counts_available: bool,
|
|
pub validation_events_indexed: bool,
|
|
}
|
|
|
|
impl ArtifactManifestSummary {
|
|
pub fn artifact_paths(&self) -> BTreeMap<String, String> {
|
|
self.files
|
|
.iter()
|
|
.map(|(name, item)| (name.clone(), item.path.clone()))
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
pub fn build_artifact_manifest(
|
|
run_dir: &Path,
|
|
query_audit: Option<&Value>,
|
|
) -> QueryDbResult<ArtifactManifestSummary> {
|
|
let mut manifest = ArtifactManifestSummary {
|
|
query_audit: query_audit.map(query_audit_summary),
|
|
cir_counts_available: false,
|
|
validation_events_indexed: false,
|
|
..ArtifactManifestSummary::default()
|
|
};
|
|
for name in [
|
|
"report.json",
|
|
"input.cir",
|
|
"result.cir",
|
|
"result.ccr",
|
|
"stage-timing.json",
|
|
"run-summary.json",
|
|
] {
|
|
let path = run_dir.join(name);
|
|
if path.exists() {
|
|
manifest
|
|
.files
|
|
.insert(name.to_string(), file_summary(&path)?);
|
|
}
|
|
}
|
|
if let Some(events_path) = manifest
|
|
.query_audit
|
|
.as_ref()
|
|
.and_then(|query_audit| query_audit.events_path.as_ref())
|
|
{
|
|
let path = sidecar_path(run_dir, events_path);
|
|
if path.exists() {
|
|
manifest
|
|
.files
|
|
.insert("validationEvents".to_string(), file_summary(&path)?);
|
|
}
|
|
}
|
|
Ok(manifest)
|
|
}
|
|
|
|
fn file_summary(path: &Path) -> QueryDbResult<ArtifactFileSummary> {
|
|
let metadata = fs::metadata(path)?;
|
|
let modified_unix_secs = metadata
|
|
.modified()
|
|
.ok()
|
|
.and_then(|time| time.duration_since(std::time::UNIX_EPOCH).ok())
|
|
.map(|duration| duration.as_secs());
|
|
Ok(ArtifactFileSummary {
|
|
path: path.display().to_string(),
|
|
size_bytes: Some(metadata.len()),
|
|
modified_unix_secs,
|
|
})
|
|
}
|
|
|
|
fn query_audit_summary(value: &Value) -> QueryAuditArtifactSummary {
|
|
QueryAuditArtifactSummary {
|
|
status: json_str(value, &["status"]).map(str::to_string),
|
|
events_path: json_str(value, &["eventsPath"])
|
|
.or_else(|| json_str(value, &["events_path"]))
|
|
.map(str::to_string),
|
|
events_count: json_u64(value, &["eventsCount"])
|
|
.or_else(|| json_u64(value, &["events_count"])),
|
|
events_sha256: json_str(value, &["eventsSha256"])
|
|
.or_else(|| json_str(value, &["events_sha256"]))
|
|
.map(str::to_string),
|
|
writer_version: json_u64(value, &["writerVersion"])
|
|
.or_else(|| json_u64(value, &["writer_version"])),
|
|
}
|
|
}
|
|
|
|
pub fn sidecar_path(run_dir: &Path, path: &str) -> PathBuf {
|
|
let path = PathBuf::from(path);
|
|
if path.is_absolute() {
|
|
path
|
|
} else {
|
|
run_dir.join(path)
|
|
}
|
|
}
|
|
|
|
fn json_str<'a>(value: &'a Value, path: &[&str]) -> Option<&'a str> {
|
|
let mut current = value;
|
|
for key in path {
|
|
current = current.get(*key)?;
|
|
}
|
|
current.as_str()
|
|
}
|
|
|
|
fn json_u64(value: &Value, path: &[&str]) -> Option<u64> {
|
|
let mut current = value;
|
|
for key in path {
|
|
current = current.get(*key)?;
|
|
}
|
|
current.as_u64()
|
|
}
|