#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(tag = "type", content = "kind")]
#[non_exhaustive]
pub enum ProvenanceLabel {
External(ExternalKind),
RecallReEntry,
ModelDerived,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[non_exhaustive]
pub enum ExternalKind {
UserAsserted,
ExternalFirstHand,
}
impl ProvenanceLabel {
pub fn is_cheap_path_eligible(&self) -> bool {
matches!(self, Self::External(_))
}
pub fn is_recall_reentry(&self) -> bool {
matches!(self, Self::RecallReEntry)
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ExternalAnchor {
pub nearest_external_anchor: Option<crate::identity::ClaimRef>,
pub derivation_depth: u32,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn external_user_asserted_is_cheap_path_eligible() {
let p = ProvenanceLabel::External(ExternalKind::UserAsserted);
assert!(p.is_cheap_path_eligible());
assert!(!p.is_recall_reentry());
}
#[test]
fn external_first_hand_is_cheap_path_eligible() {
let p = ProvenanceLabel::External(ExternalKind::ExternalFirstHand);
assert!(p.is_cheap_path_eligible());
assert!(!p.is_recall_reentry());
}
#[test]
fn recall_reentry_is_not_cheap_path_eligible() {
let p = ProvenanceLabel::RecallReEntry;
assert!(!p.is_cheap_path_eligible());
assert!(p.is_recall_reentry());
}
#[test]
fn model_derived_is_neither() {
let p = ProvenanceLabel::ModelDerived;
assert!(!p.is_cheap_path_eligible());
assert!(!p.is_recall_reentry());
}
#[test]
fn provenance_label_round_trip_serde() {
let labels = [
ProvenanceLabel::External(ExternalKind::UserAsserted),
ProvenanceLabel::External(ExternalKind::ExternalFirstHand),
ProvenanceLabel::RecallReEntry,
ProvenanceLabel::ModelDerived,
];
for label in &labels {
let json = serde_json::to_string(label).unwrap();
let back: ProvenanceLabel = serde_json::from_str(&json).unwrap();
assert_eq!(label, &back);
}
}
#[test]
fn provenance_label_python_friendly_json_shapes() {
let ext_ua = ProvenanceLabel::External(ExternalKind::UserAsserted);
let json = serde_json::to_string(&ext_ua).unwrap();
assert_eq!(json, r#"{"type":"External","kind":"UserAsserted"}"#);
let back: ProvenanceLabel = serde_json::from_str(&json).unwrap();
assert_eq!(ext_ua, back);
let ext_fh = ProvenanceLabel::External(ExternalKind::ExternalFirstHand);
let json = serde_json::to_string(&ext_fh).unwrap();
assert_eq!(json, r#"{"type":"External","kind":"ExternalFirstHand"}"#);
let back: ProvenanceLabel = serde_json::from_str(&json).unwrap();
assert_eq!(ext_fh, back);
let rre = ProvenanceLabel::RecallReEntry;
let json = serde_json::to_string(&rre).unwrap();
assert_eq!(json, r#"{"type":"RecallReEntry"}"#);
let back: ProvenanceLabel = serde_json::from_str(&json).unwrap();
assert_eq!(rre, back);
let md = ProvenanceLabel::ModelDerived;
let json = serde_json::to_string(&md).unwrap();
assert_eq!(json, r#"{"type":"ModelDerived"}"#);
let back: ProvenanceLabel = serde_json::from_str(&json).unwrap();
assert_eq!(md, back);
}
#[test]
fn external_anchor_round_trip_serde() {
let anchor = ExternalAnchor {
nearest_external_anchor: Some(crate::identity::ClaimRef::new_random()),
derivation_depth: 2,
};
let json = serde_json::to_string(&anchor).unwrap();
let back: ExternalAnchor = serde_json::from_str(&json).unwrap();
assert_eq!(anchor, back);
}
}