use serde::{Deserialize, Serialize};
use crate::event::{Event, Seq};
use crate::manifest::AttributionManifest;
use crate::time::Timestamp;
use crate::token::Token;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", tag = "kind")]
pub enum Change {
Revoked,
Superseded {
by: Token,
},
ExpirySet {
expires_at: Option<Timestamp>,
},
CampaignSet {
campaign: Option<String>,
},
LabelSet {
key: String,
value: String,
},
LabelUnset {
key: String,
},
}
impl Change {
#[must_use]
pub fn is_lifecycle(&self) -> bool {
matches!(
self,
Self::Revoked | Self::Superseded { .. } | Self::ExpirySet { .. }
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", tag = "record")]
pub enum LogRecord {
Minted {
manifest: Box<AttributionManifest>,
},
Mutation {
token: Token,
at: Timestamp,
seq: Seq,
change: Change,
},
Event(Event),
}
impl LogRecord {
#[must_use]
pub fn token(&self) -> Token {
match self {
Self::Minted { manifest } => manifest.token,
Self::Mutation { token, .. } => *token,
Self::Event(e) => e.token,
}
}
#[must_use]
pub fn seq(&self) -> Seq {
match self {
Self::Minted { .. } => Seq(0),
Self::Mutation { seq, .. } => *seq,
Self::Event(e) => e.seq,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{CanonicalUrl, Channel, MintOptions, MintSpec, Sharer};
#[test]
fn lifecycle_split_is_exact() {
assert!(Change::Revoked.is_lifecycle());
assert!(Change::Superseded {
by: Token::parse("abc").unwrap()
}
.is_lifecycle());
assert!(Change::ExpirySet { expires_at: None }.is_lifecycle());
assert!(!Change::CampaignSet { campaign: None }.is_lifecycle());
assert!(!Change::LabelSet {
key: "k".into(),
value: "v".into()
}
.is_lifecycle());
assert!(!Change::LabelUnset { key: "k".into() }.is_lifecycle());
}
#[test]
fn record_identity_and_serde() {
let mut entropy = |buf: &mut [u8]| {
buf.fill(3);
Ok(())
};
let m = crate::mint(
MintSpec::new(
CanonicalUrl::new("ws://a/b").unwrap(),
Sharer::new("lead").unwrap(),
Channel::subagent_general(),
),
&MintOptions::default(),
&mut entropy,
crate::Timestamp::from_unix_ms(1),
)
.unwrap();
let token = m.token;
let rec = LogRecord::Minted {
manifest: Box::new(m),
};
assert_eq!(rec.token(), token);
assert_eq!(rec.seq(), Seq(0));
let json = serde_json::to_string(&rec).unwrap();
let back: LogRecord = serde_json::from_str(&json).unwrap();
assert_eq!(back, rec);
assert!(
json.contains("\"record\":\"minted\""),
"tagged for the JSONL wire format"
);
}
}