messagebird_async/sms/
typedetails.rs

1use super::*;
2use std::collections::{hash_set::Iter, HashSet};
3use std::fmt;
4
5use serde::de::{Deserialize, Deserializer, MapAccess, Visitor};
6use serde::ser::{Serialize, SerializeMap, Serializer};
7
8/// Type Details
9///
10/// Additional message details for the SMS message when passing and querying the MessageBird system.
11#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
12pub enum TypeDetail {
13    /// additional user data specific bytes
14    /// FIXME should not be a string but a proper type include serde impl according to https://en.wikipedia.org/wiki/User_Data_Header but is better to be done in a separate crate
15    #[serde(rename = "udh")]
16    UserDataHeader(String),
17}
18
19use std::hash::{Hash, Hasher};
20
21impl Hash for TypeDetail {
22    fn hash<H: Hasher>(&self, state: &mut H) {
23        match self {
24            TypeDetail::UserDataHeader(_) => 1.hash(state),
25            // expiermental: _ => #[allow(unreachable_code)] unreachable!("Mising implementation for struct variant"),
26        }
27    }
28}
29
30impl TypeDetail {
31    pub fn try_from(src: (String, String)) -> Result<Self, MessageBirdError> {
32        match src.0.as_str() {
33            "udh" => Ok(TypeDetail::UserDataHeader(src.1)),
34            x => Err(MessageBirdError::TypeError {
35                msg: format!("Unknown TypeDetail \"{}\"", x),
36            }),
37        }
38    }
39
40    pub fn as_tuple(self) -> (String, String) {
41        match self {
42            TypeDetail::UserDataHeader(x) => (String::from("udh"), x),
43            // expiermental: _ => #[allow(unreachable_code)] unreachable!("tuple conversion"),
44        }
45    }
46}
47
48/// HashSet of type details
49///
50///
51#[derive(Clone, Debug, Eq, PartialEq)]
52pub struct TypeDetails {
53    inner: HashSet<TypeDetail>,
54}
55
56impl TypeDetails {
57    pub fn new() -> Self {
58        Self {
59            // TODO make sure the keys are unique, possibly implement Hash trait on TypeDetail manually
60            inner: HashSet::new(),
61        }
62    }
63
64    /// add a new type detail to the set of type details
65    pub fn add(&mut self, td: TypeDetail) {
66        self.inner.insert(td);
67    }
68
69    /// iterate over all type details
70    pub fn iter(&mut self) -> Iter<TypeDetail> {
71        self.inner.iter()
72    }
73}
74
75impl Default for TypeDetails {
76    fn default() -> Self {
77        Self::new()
78    }
79}
80
81impl Serialize for TypeDetails {
82    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
83    where
84        S: Serializer,
85    {
86        let mut map = serializer.serialize_map(Some(self.inner.len()))?;
87        for x in &self.inner {
88            let (k, v) = x.clone().as_tuple();
89            map.serialize_entry(&k, &v)?;
90        }
91        map.end()
92    }
93}
94
95struct TypeDetailsVisitor;
96
97impl<'de> Visitor<'de> for TypeDetailsVisitor {
98    type Value = TypeDetails;
99
100    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
101        formatter.write_str("a TypeDetails Map")
102    }
103
104    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
105    where
106        M: MapAccess<'de>,
107    {
108        let mut set = TypeDetails::new();
109
110        while let Some((key, value)) = access.next_entry()? {
111            let p: (String, String) = (key, value);
112            println!("TypeDetail key: {:?} value: {:?}", p.0, p.1);
113            // TODO map error properly
114            let td = TypeDetail::try_from(p).expect("Expected a valid type detail");
115            set.add(td);
116        }
117
118        Ok(set)
119    }
120}
121
122impl<'de> Deserialize<'de> for TypeDetails {
123    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
124    where
125        D: Deserializer<'de>,
126    {
127        deserializer.deserialize_map(TypeDetailsVisitor)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    static RAW_TYPE_DETAILS_EMPTY: &str = r#"
136{}
137"#;
138    static RAW_TYPE_DETAILS_WITH_UDH: &str = r#"
139{
140    "udh" : "UserDataHeaderContent:)"
141}
142"#;
143
144    lazy_static! {
145        static ref DETAILS: TypeDetails = {
146            let mut details = TypeDetails::new();
147            details.add(TypeDetail::UserDataHeader("some".to_string()));
148            details
149        };
150    }
151
152    serde_roundtrip!(serde_typedetails_empty, TypeDetails, TypeDetails::new());
153    deser_roundtrip!(deser_typedetails_empty, TypeDetails, RAW_TYPE_DETAILS_EMPTY);
154
155    serde_roundtrip!(serde_typedetails_with_udh, TypeDetails, DETAILS.clone());
156    deser_roundtrip!(
157        deser_typedetails_with_udh,
158        TypeDetails,
159        RAW_TYPE_DETAILS_WITH_UDH
160    );
161
162    serde_roundtrip!(
163        serde_typedetail_udh,
164        TypeDetail,
165        TypeDetail::UserDataHeader("some".to_string())
166    );
167
168}