20260616 增加产品UI查询服务
This commit is contained in:
139
src/query/artifact_manifest.rs
Normal file
139
src/query/artifact_manifest.rs
Normal file
@@ -0,0 +1,139 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user