Skip to main content

caldir_core/event/
organizer.rs

1use 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}