Skip to main content

sip2/
message_json.rs

1//! JSON serializatoin routintes for SIP messages.
2use super::Message;
3use std::collections::HashMap;
4use std::error;
5use std::fmt;
6
7/// Errors related specifically to SIP <=> JSON routines
8#[derive(Debug)]
9pub enum SipJsonError {
10    /// Data does not contain the correct content, e.g. sip message code.
11    MessageFormatError(String),
12
13    /// Data cannot be successfully minipulated as JSON
14    JsonError(serde_json::Error),
15}
16
17impl error::Error for SipJsonError {
18    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
19        match *self {
20            SipJsonError::JsonError(ref err) => Some(err),
21            _ => None,
22        }
23    }
24}
25
26impl fmt::Display for SipJsonError {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        match self {
29            SipJsonError::JsonError(err) => err.fmt(f),
30            SipJsonError::MessageFormatError(s) => {
31                write!(f, "SIP message could not be translated to/from JSON: {}", s)
32            }
33        }
34    }
35}
36
37impl Message {
38    /// Translate a SIP Message into a JSON object.
39    ///
40    /// ```
41    /// use sip2::{Message, Field, FixedField};
42    /// use sip2::spec;
43    ///
44    /// let msg = Message::new(
45    ///     &spec::M_LOGIN,
46    ///     vec![
47    ///         FixedField::new(&spec::FF_UID_ALGO, "0").unwrap(),
48    ///         FixedField::new(&spec::FF_PWD_ALGO, "0").unwrap(),
49    ///     ],
50    ///     vec![
51    ///         Field::new(spec::F_LOGIN_UID.code, "sip_username"),
52    ///         Field::new(spec::F_LOGIN_PWD.code, "sip_password"),
53    ///     ]
54    /// );
55    ///
56    /// let json_val = msg.to_json_value();
57    /// let expected = serde_json::json!({
58    ///   "code":"93",
59    ///   "fixed_fields":["0","0"],
60    ///   "fields":[{"CN":"sip_username"},{"CO":"sip_password"}]});
61    ///
62    /// assert_eq!(expected, json_val);
63    /// ```
64    pub fn to_json_value(&self) -> serde_json::Value {
65        let ff: Vec<String> = self
66            .fixed_fields()
67            .iter()
68            .map(|f| f.value().to_string())
69            .collect();
70
71        let mut fields: Vec<HashMap<String, String>> = Vec::new();
72
73        for f in self.fields().iter() {
74            let mut map = HashMap::new();
75            map.insert(f.code().to_string(), f.value().to_string());
76            fields.push(map);
77        }
78
79        serde_json::json!({
80            "code": self.spec().code,
81            "fixed_fields": ff,
82            "fields": fields
83        })
84    }
85
86    /// Translate a SIP Message into a JSON string.
87    ///
88    /// ```
89    /// use sip2::{Message, Field, FixedField};
90    /// use sip2::spec;
91    ///
92    /// let msg = Message::new(
93    ///     &spec::M_LOGIN,
94    ///     vec![
95    ///         FixedField::new(&spec::FF_UID_ALGO, "0").unwrap(),
96    ///         FixedField::new(&spec::FF_PWD_ALGO, "0").unwrap(),
97    ///     ],
98    ///     vec![
99    ///         Field::new(spec::F_LOGIN_UID.code, "sip_username"),
100    ///         Field::new(spec::F_LOGIN_PWD.code, "sip_password"),
101    ///     ]
102    /// );
103    ///
104    /// let json_str = msg.to_json();
105    ///
106    /// // Comparing JSON strings is nontrivial with hashes.
107    /// // Assume completion means success.  See to_json_value() for
108    /// // more rigorous testing.
109    /// assert_eq!(true, true);
110    /// ```
111    pub fn to_json(&self) -> String {
112        serde_json::to_string(&self.to_json_value()).expect("JSON serialization")
113    }
114
115    /// Translate a JSON object into a SIP Message.
116    ///
117    /// Field and FixedField values must be JSON strings or numbers.
118    ///
119    /// ```
120    /// use sip2::{Message, Field, FixedField};
121    /// use sip2::spec;
122    ///
123    /// let expected = Message::new(
124    ///     &spec::M_LOGIN,
125    ///     vec![
126    ///         FixedField::new(&spec::FF_UID_ALGO, "0").unwrap(),
127    ///         FixedField::new(&spec::FF_PWD_ALGO, "0").unwrap(),
128    ///     ],
129    ///     vec![
130    ///         Field::new(spec::F_LOGIN_UID.code, "sip_username"),
131    ///         Field::new(spec::F_LOGIN_PWD.code, "sip_password"),
132    ///     ]
133    /// );
134    ///
135    /// let json_val = serde_json::json!({
136    ///   "code":"93",
137    ///   "fixed_fields":["0",0],
138    ///   "fields":[{"CN":"sip_username"},{"CO":"sip_password"}]});
139    ///
140    /// let msg = Message::from_json_value(json_val).unwrap();
141    ///
142    /// assert_eq!(expected, msg);
143    ///
144    /// let m = Message::from_json_value(serde_json::json!({"code":"93","fixed_fields":[{"bad":"news"}]}));
145    /// assert!(m.is_err());
146    /// ```
147    pub fn from_json_value(json_value: serde_json::Value) -> Result<Message, SipJsonError> {
148        // Start with a message that's just the code plus fixed fields
149        // as a SIP string.
150        let mut strbuf = json_value["code"].as_str().ok_or_else(|| {
151            SipJsonError::MessageFormatError("Message requires a code".to_string())
152        })?.to_string();
153
154        if let Some(ff_array) = json_value["fixed_fields"].as_array() {
155            for ff in ff_array {
156                if let Some(s) = ff.as_str() {
157                    strbuf += s;
158                } else if ff.is_number() {
159                    strbuf += &format!("{ff}");
160                } else {
161                    return Err(SipJsonError::MessageFormatError(format!(
162                        "Fixed field values must be JSON strings or numbers: {}",
163                        serde_json::to_string(ff).unwrap_or_else(|e| e.to_string())
164                    )));
165                }
166            }
167        }
168
169        // Since we're creating this partial SIP string from raw
170        // JSON values and the buffer this far should not contain
171        // any separater chars, clean it up before parsing as SIP.
172        strbuf = super::util::sip_string(&strbuf);
173
174        let mut msg = Message::from_sip(&strbuf).map_err(|e| {
175            SipJsonError::MessageFormatError(format!(
176                "Message is not correctly formatted: {e} {}",
177                serde_json::to_string(&json_value).unwrap_or_else(|e| e.to_string())
178            ))
179        })?;
180
181        if let Some(fields_array) = json_value["fields"].as_array() {
182            for field in fields_array {
183                if let Some(obj) = field.as_object() {
184                    for (code, value) in obj.iter() {
185                        if let Some(s) = value.as_str() {
186                            msg.add_field(code, s);
187                        } else if value.is_number() {
188                            msg.add_field(code, &format!("{value}"));
189                        } else {
190                            return Err(SipJsonError::MessageFormatError(format!(
191                                "Message is not correctly formatted: {}",
192                                serde_json::to_string(&json_value).unwrap_or_else(|e| e.to_string())
193                            )));
194                        }
195                    }
196                }
197            }
198        }
199
200        Ok(msg)
201    }
202
203    /// Translate a JSON string into a SIP Message.
204    ///
205    /// ```
206    /// use sip2::{Message, Field, FixedField};
207    /// use sip2::spec;
208    ///
209    /// let expected = Message::new(
210    ///     &spec::M_LOGIN,
211    ///     vec![
212    ///         FixedField::new(&spec::FF_UID_ALGO, "0").unwrap(),
213    ///         FixedField::new(&spec::FF_PWD_ALGO, "0").unwrap(),
214    ///     ],
215    ///     vec![
216    ///         Field::new(spec::F_LOGIN_UID.code, "sip_username"),
217    ///         Field::new(spec::F_LOGIN_PWD.code, "sip_password"),
218    ///     ]
219    /// );
220    ///
221    /// let json_str = r#"
222    ///   {
223    ///     "code":"93",
224    ///     "fixed_fields":["0","0"],
225    ///     "fields":[{"CN":"sip_username"},{"CO":"sip_password"}]
226    ///   }
227    /// "#;
228    ///
229    /// let msg = Message::from_json(&json_str).unwrap();
230    ///
231    /// assert_eq!(expected, msg);
232    /// ```
233    pub fn from_json(msg_json: &str) -> Result<Message, SipJsonError> {
234        let json_value: serde_json::Value = match serde_json::from_str(msg_json) {
235            Ok(v) => v,
236            Err(e) => {
237                return Err(SipJsonError::JsonError(e));
238            }
239        };
240
241        Message::from_json_value(json_value)
242    }
243}