use serde::{Deserialize, Serialize};
pub type PeerId = u64;
pub const AUTHOR_SOURCES: &[&str] = &[SOURCE_HAND, SOURCE_LIBRARY, SOURCE_AI];
pub const SOURCE_HAND: &str = "hand";
pub const SOURCE_LIBRARY: &str = "library";
pub const SOURCE_AI: &str = "ai";
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Author {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub peer: Option<PeerId>,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
}
impl Author {
pub fn named(name: impl Into<String>) -> Self {
Self {
peer: None,
name: name.into(),
source: None,
}
}
pub fn known_source(&self) -> Option<&'static str> {
let source = self.source.as_deref()?;
AUTHOR_SOURCES.iter().copied().find(|s| *s == source)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn source_rides_the_wire_only_when_set() {
let plain = Author::named("maya");
assert_eq!(serde_json::to_string(&plain).unwrap(), r#"{"name":"maya"}"#);
let labelled = Author {
source: Some(SOURCE_AI.into()),
..Author::named("blueprint")
};
let json = serde_json::to_string(&labelled).unwrap();
assert_eq!(json, r#"{"name":"blueprint","source":"ai"}"#);
let back: Author = serde_json::from_str(&json).unwrap();
assert_eq!(back, labelled);
assert_eq!(back.known_source(), Some("ai"));
}
#[test]
fn an_unknown_label_is_kept_but_reads_as_none() {
let back: Author = serde_json::from_str(r#"{"name":"x","source":"template"}"#).unwrap();
assert_eq!(back.source.as_deref(), Some("template"));
assert_eq!(back.known_source(), None);
assert!(serde_json::to_string(&back).unwrap().contains("template"));
}
}