use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct StoreMeta {
pub schema: String,
pub store_hash: String,
pub recipe_hash: String,
pub input_hashes: Vec<String>,
pub arch: String,
pub provider: String,
pub created_at: String,
pub generator: String,
#[serde(default)]
pub references: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provenance: Option<Provenance>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Provenance {
pub origin_provider: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub origin_ref: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub origin_hash: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub derived_from: Option<String>,
#[serde(default)]
pub derivation_depth: u32,
}
pub fn new_meta(
store_hash: &str,
recipe_hash: &str,
input_hashes: &[String],
arch: &str,
provider: &str,
) -> StoreMeta {
use crate::tripwire::eventlog::now_iso8601;
StoreMeta {
schema: "1.0".to_string(),
store_hash: store_hash.to_string(),
recipe_hash: recipe_hash.to_string(),
input_hashes: input_hashes.to_vec(),
arch: arch.to_string(),
provider: provider.to_string(),
created_at: now_iso8601(),
generator: format!("forjar {}", env!("CARGO_PKG_VERSION")),
references: Vec::new(),
provenance: None,
}
}
pub fn write_meta(dir: &Path, meta: &StoreMeta) -> Result<(), String> {
if let Some(parent) = dir.parent() {
std::fs::create_dir_all(parent)
.map_err(|e| format!("cannot create dir {}: {}", parent.display(), e))?;
}
std::fs::create_dir_all(dir)
.map_err(|e| format!("cannot create dir {}: {}", dir.display(), e))?;
let path = dir.join("meta.yaml");
let yaml = serde_yaml_ng::to_string(meta).map_err(|e| format!("serialize meta error: {e}"))?;
let tmp_path = path.with_extension("yaml.tmp");
std::fs::write(&tmp_path, &yaml)
.map_err(|e| format!("cannot write {}: {}", tmp_path.display(), e))?;
std::fs::rename(&tmp_path, &path).map_err(|e| {
format!(
"cannot rename {} → {}: {}",
tmp_path.display(),
path.display(),
e
)
})?;
Ok(())
}
pub fn read_meta(dir: &Path) -> Result<StoreMeta, String> {
let path = dir.join("meta.yaml");
let content = std::fs::read_to_string(&path)
.map_err(|e| format!("cannot read {}: {}", path.display(), e))?;
serde_yaml_ng::from_str(&content)
.map_err(|e| format!("invalid meta.yaml {}: {}", path.display(), e))
}