caldir_core/event/
organizer.rs1use icalendar::Property;
2use std::fmt;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct Organizer {
6 pub email: String,
7 pub name: Option<String>,
8}
9
10impl Organizer {
11 pub fn new(email: impl Into<String>) -> Self {
12 Organizer {
13 email: email.into(),
14 name: None,
15 }
16 }
17}
18
19impl fmt::Display for Organizer {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match &self.name {
22 Some(name) if !name.is_empty() => write!(f, "{} ({})", name, self.email),
23 _ => write!(f, "{}", self.email),
24 }
25 }
26}
27
28impl From<&Organizer> for Property {
29 fn from(value: &Organizer) -> Self {
30 let mut prop = Property::new("ORGANIZER", format!("mailto:{}", value.email));
31 if let Some(name) = &value.name {
32 prop.add_parameter("CN", name);
33 }
34 prop.done()
35 }
36}
37
38impl From<&Property> for Organizer {
39 fn from(value: &Property) -> Self {
40 let email = value
41 .value()
42 .strip_prefix("mailto:")
43 .unwrap_or(value.value())
44 .to_string();
45 let name = value.params().get("CN").map(|p| p.value().to_string());
46 Organizer { email, name }
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn parses_email_from_mailto_value() {
56 let prop = Property::new("ORGANIZER", "mailto:alice@example.com").done();
57
58 let organizer = Organizer::from(&prop);
59
60 assert_eq!(organizer.email, "alice@example.com");
61 assert_eq!(organizer.name, None);
62 }
63
64 #[test]
65 fn parses_email_without_mailto_prefix() {
66 let prop = Property::new("ORGANIZER", "alice@example.com").done();
67
68 let organizer = Organizer::from(&prop);
69
70 assert_eq!(organizer.email, "alice@example.com");
71 }
72
73 #[test]
74 fn parses_cn_parameter_as_name() {
75 let mut prop = Property::new("ORGANIZER", "mailto:alice@example.com");
76 prop.add_parameter("CN", "Alice Smith");
77
78 let organizer = Organizer::from(&prop.done());
79
80 assert_eq!(organizer.name.as_deref(), Some("Alice Smith"));
81 }
82
83 #[test]
84 fn writes_email_with_mailto_prefix() {
85 let organizer = Organizer::new("alice@example.com");
86
87 let prop = Property::from(&organizer);
88
89 assert_eq!(prop.value(), "mailto:alice@example.com");
90 assert!(prop.params().get("CN").is_none());
91 }
92
93 #[test]
94 fn writes_name_as_cn_parameter() {
95 let organizer = Organizer {
96 email: "alice@example.com".to_string(),
97 name: Some("Alice Smith".to_string()),
98 };
99
100 let prop = Property::from(&organizer);
101
102 assert_eq!(
103 prop.params().get("CN").map(|p| p.value()),
104 Some("Alice Smith")
105 );
106 }
107
108 #[test]
109 fn display_shows_name_and_email_when_name_present() {
110 let organizer = Organizer {
111 email: "alice@example.com".to_string(),
112 name: Some("Alice Smith".to_string()),
113 };
114
115 assert_eq!(organizer.to_string(), "Alice Smith (alice@example.com)");
116 }
117
118 #[test]
119 fn display_falls_back_to_email_when_name_missing_or_empty() {
120 let no_name = Organizer::new("alice@example.com");
121 assert_eq!(no_name.to_string(), "alice@example.com");
122
123 let empty_name = Organizer {
124 email: "alice@example.com".to_string(),
125 name: Some(String::new()),
126 };
127 assert_eq!(empty_name.to_string(), "alice@example.com");
128 }
129}