use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Subject {
pub did: String,
pub at_uri: Option<String>,
pub cid: Option<String>,
}
impl Subject {
pub fn account(did: impl Into<String>) -> Self {
Self {
did: did.into(),
at_uri: None,
cid: None,
}
}
pub fn record(did: impl Into<String>, at_uri: impl Into<String>, cid: Option<String>) -> Self {
Self {
did: did.into(),
at_uri: Some(at_uri.into()),
cid,
}
}
pub fn is_account_level(&self) -> bool {
self.at_uri.is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn account_constructor_omits_uri_and_cid() {
let s = Subject::account("did:plc:abc");
assert_eq!(s.did, "did:plc:abc");
assert!(s.at_uri.is_none());
assert!(s.cid.is_none());
assert!(s.is_account_level());
}
#[test]
fn record_constructor_with_cid_pins_version() {
let s = Subject::record(
"did:plc:abc",
"at://did:plc:abc/app.bsky.feed.post/xyz",
Some("bafy123".to_string()),
);
assert_eq!(s.did, "did:plc:abc");
assert_eq!(
s.at_uri.as_deref(),
Some("at://did:plc:abc/app.bsky.feed.post/xyz")
);
assert_eq!(s.cid.as_deref(), Some("bafy123"));
assert!(!s.is_account_level());
}
#[test]
fn record_constructor_without_cid_targets_all_versions() {
let s = Subject::record(
"did:plc:abc",
"at://did:plc:abc/app.bsky.feed.post/xyz",
None,
);
assert!(s.cid.is_none());
assert!(s.at_uri.is_some());
assert!(!s.is_account_level());
}
#[test]
fn subject_serde_roundtrips() {
let s = Subject::record(
"did:plc:abc",
"at://did:plc:abc/app.bsky.feed.post/xyz",
Some("bafy123".into()),
);
let json = serde_json::to_string(&s).unwrap();
let back: Subject = serde_json::from_str(&json).unwrap();
assert_eq!(s, back);
}
}