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}