use camino::Utf8PathBuf;
use serde::{Deserialize, Serialize};
pub const SCHEMA: &str = "cordance-evidence-map.v1";
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EvidenceMap {
pub schema: String,
pub pack_id: String,
pub rules: Vec<EvidenceEntry>,
pub outputs: Vec<OutputEvidence>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EvidenceEntry {
pub rule_id: String,
pub text: String,
pub sources: Vec<EvidenceSource>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EvidenceSource {
pub path: Utf8PathBuf,
#[serde(skip_serializing_if = "Option::is_none")]
pub line_range: Option<(u32, u32)>,
pub kind: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OutputEvidence {
pub path: Utf8PathBuf,
pub sha256: String,
pub source_ids: Vec<String>,
}
impl EvidenceMap {
#[must_use]
pub fn empty(pack_id: String) -> Self {
Self {
schema: SCHEMA.into(),
pack_id,
rules: vec![],
outputs: vec![],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_map_has_v1_schema() {
let m = EvidenceMap::empty("abc".into());
assert_eq!(m.schema, SCHEMA);
assert_eq!(m.pack_id, "abc");
assert!(m.rules.is_empty());
assert!(m.outputs.is_empty());
}
#[test]
fn evidence_map_round_trips() {
let m = EvidenceMap {
schema: SCHEMA.into(),
pack_id: "pid".into(),
rules: vec![EvidenceEntry {
rule_id: "R-1".into(),
text: "rule text".into(),
sources: vec![EvidenceSource {
path: "doctrine/principles/build.md".into(),
line_range: Some((10, 20)),
kind: "doctrine".into(),
}],
}],
outputs: vec![OutputEvidence {
path: "AGENTS.md".into(),
sha256: "deadbeef".into(),
source_ids: vec!["project_readme:README.md".into()],
}],
};
let s = serde_json::to_string(&m).expect("ser");
let back: EvidenceMap = serde_json::from_str(&s).expect("de");
assert_eq!(back.pack_id, "pid");
assert_eq!(back.rules.len(), 1);
assert_eq!(back.outputs.len(), 1);
}
#[test]
fn line_range_skipped_when_none() {
let src = EvidenceSource {
path: "doctrine/x.md".into(),
line_range: None,
kind: "doctrine".into(),
};
let s = serde_json::to_string(&src).expect("ser");
assert!(!s.contains("line_range"));
}
}