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(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(ref 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 /// use json;
44 ///
45 /// let msg = Message::new(
46 /// &spec::M_LOGIN,
47 /// vec![
48 /// FixedField::new(&spec::FF_UID_ALGO, "0").unwrap(),
49 /// FixedField::new(&spec::FF_PWD_ALGO, "0").unwrap(),
50 /// ],
51 /// vec![
52 /// Field::new(spec::F_LOGIN_UID.code, "sip_username"),
53 /// Field::new(spec::F_LOGIN_PWD.code, "sip_password"),
54 /// ]
55 /// );
56 ///
57 /// let json_val = msg.to_json_value();
58 /// let expected = json::object!{
59 /// "code":"93",
60 /// "fixed_fields":["0","0"],
61 /// "fields":[{"CN":"sip_username"},{"CO":"sip_password"}]};
62 ///
63 /// assert_eq!(expected, json_val);
64 /// ```
65 pub fn to_json_value(&self) -> json::JsonValue {
66 let ff: Vec<String> = self
67 .fixed_fields()
68 .iter()
69 .map(|f| f.value().to_string())
70 .collect();
71
72 let mut fields: Vec<HashMap<String, String>> = Vec::new();
73
74 for f in self.fields().iter() {
75 let mut map = HashMap::new();
76 map.insert(f.code().to_string(), f.value().to_string());
77 fields.push(map);
78 }
79
80 json::object! {
81 "code": self.spec().code,
82 "fixed_fields": ff,
83 "fields": fields
84 }
85 }
86
87 /// Translate a SIP Message into a JSON string.
88 ///
89 /// ```
90 /// use sip2::{Message, Field, FixedField};
91 /// use sip2::spec;
92 ///
93 /// let msg = Message::new(
94 /// &spec::M_LOGIN,
95 /// vec![
96 /// FixedField::new(&spec::FF_UID_ALGO, "0").unwrap(),
97 /// FixedField::new(&spec::FF_PWD_ALGO, "0").unwrap(),
98 /// ],
99 /// vec![
100 /// Field::new(spec::F_LOGIN_UID.code, "sip_username"),
101 /// Field::new(spec::F_LOGIN_PWD.code, "sip_password"),
102 /// ]
103 /// );
104 ///
105 /// let json_str = msg.to_json();
106 ///
107 /// // Comparing JSON strings is nontrivial with hashes.
108 /// // Assume completion means success. See to_json_value() for
109 /// // more rigorous testing.
110 /// assert_eq!(true, true);
111 /// ```
112 pub fn to_json(&self) -> String {
113 self.to_json_value().dump()
114 }
115
116 /// Translate a JSON object into a SIP Message.
117 ///
118 /// Field and FixedField values must be JSON strings or numbers.
119 ///
120 /// ```
121 /// use sip2::{Message, Field, FixedField};
122 /// use sip2::spec;
123 /// use json;
124 ///
125 /// let expected = Message::new(
126 /// &spec::M_LOGIN,
127 /// vec![
128 /// FixedField::new(&spec::FF_UID_ALGO, "0").unwrap(),
129 /// FixedField::new(&spec::FF_PWD_ALGO, "0").unwrap(),
130 /// ],
131 /// vec![
132 /// Field::new(spec::F_LOGIN_UID.code, "sip_username"),
133 /// Field::new(spec::F_LOGIN_PWD.code, "sip_password"),
134 /// ]
135 /// );
136 ///
137 /// let json_val = json::object!{
138 /// "code":"93",
139 /// "fixed_fields":["0",0],
140 /// "fields":[{"CN":"sip_username"},{"CO":"sip_password"}]};
141 ///
142 /// let msg = Message::from_json_value(json_val).unwrap();
143 ///
144 /// assert_eq!(expected, msg);
145 ///
146 /// let m = Message::from_json_value(json::object! {"code":"93","fixed_fields":[{"bad":"news"}]});
147 /// assert!(m.is_err());
148 /// ```
149 pub fn from_json_value(mut json_value: json::JsonValue) -> Result<Message, SipJsonError> {
150 // Start with a message that's just the code plus fixed fields
151 // as a SIP string.
152 let mut strbuf = json_value["code"].take_string().ok_or_else(|| {
153 SipJsonError::MessageFormatError("Message requires a code".to_string())
154 })?;
155
156 for ff in json_value["fixed_fields"].members() {
157 if let Some(s) = ff.as_str() {
158 strbuf += s;
159 } else if ff.is_number() {
160 strbuf += &format!("{ff}");
161 } else {
162 return Err(SipJsonError::MessageFormatError(format!(
163 "Fixed field values must be JSON strings or numbers: {}",
164 ff.dump()
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 json_value.dump()
178 ))
179 })?;
180
181 for field in json_value["fields"].members() {
182 for (code, value) in field.entries() {
183 if let Some(s) = value.as_str() {
184 msg.add_field(code, s);
185 } else if value.is_number() {
186 msg.add_field(code, &format!("{value}"));
187 } else {
188 return Err(SipJsonError::MessageFormatError(format!(
189 "Message is not correctly formatted: {}",
190 json_value.dump()
191 )));
192 }
193 }
194 }
195
196 Ok(msg)
197 }
198
199 /// Translate a JSON string into a SIP Message.
200 ///
201 /// ```
202 /// use sip2::{Message, Field, FixedField};
203 /// use sip2::spec;
204 /// use json;
205 ///
206 /// let expected = Message::new(
207 /// &spec::M_LOGIN,
208 /// vec![
209 /// FixedField::new(&spec::FF_UID_ALGO, "0").unwrap(),
210 /// FixedField::new(&spec::FF_PWD_ALGO, "0").unwrap(),
211 /// ],
212 /// vec![
213 /// Field::new(spec::F_LOGIN_UID.code, "sip_username"),
214 /// Field::new(spec::F_LOGIN_PWD.code, "sip_password"),
215 /// ]
216 /// );
217 ///
218 /// let json_str = r#"
219 /// {
220 /// "code":"93",
221 /// "fixed_fields":["0","0"],
222 /// "fields":[{"CN":"sip_username"},{"CO":"sip_password"}]
223 /// }
224 /// "#;
225 ///
226 /// let msg = Message::from_json(&json_str).unwrap();
227 ///
228 /// assert_eq!(expected, msg);
229 /// ```
230 pub fn from_json(msg_json: &str) -> Result<Message, SipJsonError> {
231 let json_value: json::JsonValue = match json::parse(msg_json) {
232 Ok(v) => v,
233 Err(e) => {
234 return Err(SipJsonError::JsonError(e));
235 }
236 };
237
238 Message::from_json_value(json_value)
239 }
240}