contack/
contact_information.rs

1//! Stores information regarding contact information.
2//!
3//! Contact information specified anything that you can use
4//! to communicate with the person, such as a phone number, an email or
5//! an irc. Contack includes some added types not present in the
6//! VCard specification.
7
8use crate::contact_platform::ContactPlatform;
9
10#[cfg(feature = "read_write")]
11use crate::read_write::component::Component;
12#[cfg(feature = "read_write")]
13use crate::read_write::error::FromComponentError;
14
15/// Represents any way which a contact can be contacted.
16///
17/// This may be a phone number, an email etc. You can add
18/// any number of them to a `Contact`.
19#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
20pub struct ContactInformation {
21    /// The pid, used for representing this amongst
22    /// other `ContactInformation`s.
23    pub pid: String,
24
25    /// The Preference Value. In this implementation 0 represents that it is
26    /// neutral, not either way.
27    pub pref: u8,
28
29    /// The Value (for example johndoe@example.com)
30    pub value: String,
31
32    /// The platform which this is on
33    pub platform: ContactPlatform,
34
35    /// The type
36    pub typ: Option<Type>,
37}
38
39#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
40#[cfg_attr(
41    feature = "diesel-derive-enum",
42    derive(DbEnum),
43    DieselType = "Contack_contact_information_type",
44    db_rename = "Contack_contact_information_type"
45)]
46#[cfg_attr(
47    feature = "sqlx",
48    derive(sqlx::Type),
49    sqlx(
50        type_name = "contack_contact_information_type",
51        rename_all = "PascalCase"
52    )
53)]
54/// Reperesents the type of a `ContactInformation`.
55///
56/// The type specifies whether this is a personal
57/// or public `ContactInformation`.
58pub enum Type {
59    /// Means that it is fork work or business, ie
60    /// a git commit email.
61    Work,
62    /// Indicates that it is personal, such as a
63    /// personal phone number used on signal.
64    Home,
65}
66
67impl ContactInformation {
68    /// Creates a new `ContactInformation`
69    ///
70    /// This also auto generates a pid between 0 and 100
71    #[must_use]
72    pub fn new(string: String, platform: ContactPlatform) -> Self {
73        Self {
74            pref: 0,
75            value: string,
76            pid: format!("{}", rand::random::<u64>() % 100),
77            platform,
78            typ: None,
79        }
80    }
81}
82
83#[cfg(feature = "read_write")]
84impl From<ContactInformation> for Component {
85    fn from(ci: ContactInformation) -> Self {
86        Self {
87            // No group
88            group: None,
89
90            // Name of platform
91            name: match ci.platform {
92                ContactPlatform::Email => "EMAIL",
93                ContactPlatform::Tel => "TEL",
94                ContactPlatform::Discord => "X-DISCORD",
95                ContactPlatform::Matrix => "X-MATRIX",
96                ContactPlatform::Skype => "X-SKYPE",
97                ContactPlatform::Aim => "X-AIM",
98                ContactPlatform::Jabber => "X-JABBER",
99                ContactPlatform::Icq => "X-ICQ",
100                ContactPlatform::Groupwise => "X-GROUPWISE",
101                ContactPlatform::Gadugadu => "X-GADUGADU",
102                ContactPlatform::Unknown => "IMPP",
103            }
104            .to_string(),
105
106            // Parameters
107            parameters: {
108                // HashMap
109                let mut hashmap = std::collections::HashMap::new();
110
111                // Append the preference value.
112                if ci.pref != 0 {
113                    hashmap.insert("PREF".to_string(), ci.pref.to_string());
114                }
115
116                // Append the type
117                if let Some(typ) = ci.typ {
118                    hashmap.insert(
119                        "TYPE".to_string(),
120                        match typ {
121                            Type::Home => "home",
122                            Type::Work => "work",
123                        }
124                        .to_string(),
125                    );
126                }
127
128                // And the pid
129                hashmap.insert("PID".to_string(), ci.pid.to_string());
130
131                hashmap
132            },
133
134            values: vec![vec![ci.value]],
135        }
136    }
137}
138
139#[cfg(feature = "read_write")]
140impl TryFrom<Component> for ContactInformation {
141    type Error = FromComponentError;
142
143    fn try_from(mut comp: Component) -> Result<Self, Self::Error> {
144        Ok(Self {
145            pid: comp
146                .parameters
147                .remove("PID")
148                .unwrap_or(format!("{}", rand::random::<u64>() % 100)),
149            pref: comp.parameters.remove("PREF").map_or(Ok(0), |x| {
150                x.parse().map_err(FromComponentError::ParseIntError)
151            })?,
152            value: comp
153                .values
154                .pop()
155                .map(|mut x| x.pop())
156                .unwrap_or_default()
157                .ok_or(FromComponentError::NotEnoughValues)?,
158            platform: match comp.name.as_str() {
159                "EMAIL" => ContactPlatform::Email,
160                "TEL" => ContactPlatform::Tel,
161                "X-DISCORD" => ContactPlatform::Discord,
162                "X-MATRIX" => ContactPlatform::Matrix,
163                "X-SKYPE" => ContactPlatform::Skype,
164                "X-AIM" => ContactPlatform::Aim,
165                "X-JABBER" => ContactPlatform::Jabber,
166                "X-ICQ" => ContactPlatform::Icq,
167                "X-GROUPWISE" => ContactPlatform::Groupwise,
168                "X-GADUGADU" => ContactPlatform::Gadugadu,
169                _ => ContactPlatform::Unknown,
170            },
171            typ: comp
172                .parameters
173                .remove("TYPE")
174                .map(|x| {
175                    match x.to_lowercase().split(',').next().unwrap_or_default()
176                    {
177                        "home" => Some(Type::Home),
178                        "work" => Some(Type::Work),
179                        // Its not fair to throw an error since technically
180                        // other values may be used.
181                        _ => None,
182                    }
183                })
184                .unwrap_or_default(),
185        })
186    }
187}