rvoip_sip_core/json/mod.rs
1pub mod value;
2pub mod path;
3pub mod query;
4pub mod ext;
5
6// Re-export main types and traits
7pub use value::SipValue;
8pub use ext::SipJsonExt;
9
10use serde::Serialize;
11use serde::de::DeserializeOwned;
12use std::error::Error;
13use std::fmt;
14
15/// # JSON Representation and Access Layer for SIP Types
16///
17/// This module provides a comprehensive JSON-based interface for working with SIP types,
18/// enabling powerful, flexible access to SIP message data.
19///
20/// ## Overview
21///
22/// The SIP JSON module offers several ways to interact with SIP messages:
23///
24/// 1. **Path-based access** - Direct access via dot notation (e.g., `headers.From.display_name`)
25/// 2. **Query-based access** - Complex searches using JSONPath-like syntax (e.g., `$..display_name`)
26/// 3. **Object conversion** - Convert between SIP types and JSON structures
27/// 4. **Convenience traits** - Helper methods for common SIP header access patterns
28///
29/// ## Core Components
30///
31/// - [`SipValue`](value::SipValue) - A JSON-like value representing any SIP data
32/// - [`SipJsonExt`](SipJsonExt) - Extension trait providing JSON operations on SIP types
33/// - [`SipMessageJson`](ext::SipMessageJson) - Convenience trait for common SIP headers
34/// - [`path`](path) module - Functions for path-based access
35/// - [`query`](query) module - Functions for query-based access
36///
37/// ## Path Access vs. Query Access
38///
39/// * **Path access** is direct and specific - use when you know exactly what you're looking for
40/// * **Query access** is flexible and powerful - use when searching for patterns or exploring
41///
42/// ## Basic Usage Examples
43///
44/// ### Path-based Access
45///
46/// ```rust
47/// use rvoip_sip_core::prelude::*;
48/// use rvoip_sip_core::json::SipJsonExt;
49///
50/// # fn example() -> Option<()> {
51/// let request = RequestBuilder::invite("sip:bob@example.com").unwrap()
52/// .from("Alice", "sip:alice@example.com", Some("tag12345"))
53/// .to("Bob", "sip:bob@example.com", None)
54/// .build();
55///
56/// // Simple path access with Option return
57/// if let Some(from_display) = request.path("headers.From.display_name") {
58/// println!("From display name: {}", from_display);
59/// }
60///
61/// // Direct string access with default value
62/// let to_display = request.path_str_or("headers.To.display_name", "Unknown");
63/// let from_tag = request.path_str_or("headers.From.params[0].Tag", "No tag");
64/// # Some(())
65/// # }
66/// ```
67///
68/// ### Query-based Access
69///
70/// ```rust
71/// use rvoip_sip_core::prelude::*;
72/// use rvoip_sip_core::json::SipJsonExt;
73///
74/// # fn example() -> Option<()> {
75/// let request = RequestBuilder::invite("sip:bob@example.com").unwrap()
76/// .from("Alice", "sip:alice@example.com", Some("tag12345"))
77/// .to("Bob", "sip:bob@example.com", Some("tag6789"))
78/// .via("pc33.atlanta.com", "UDP", Some("z9hG4bK776asdhds"))
79/// .build();
80///
81/// // Find all display names in the message
82/// let display_names = request.query("$..display_name");
83/// for name in &display_names {
84/// println!("Found display name: {}", name);
85/// }
86///
87/// // Find all tags anywhere in the message
88/// let tags = request.query("$..Tag");
89/// println!("Found {} tags", tags.len());
90///
91/// // Complex queries are also possible
92/// let branches = request.query("$.headers.Via[*].params[?(@.Branch)]");
93/// for branch in &branches {
94/// println!("Via branch: {}", branch);
95/// }
96/// # Some(())
97/// # }
98/// ```
99///
100/// ### Using SIP Message Helpers
101///
102/// ```rust
103/// use rvoip_sip_core::prelude::*;
104/// use rvoip_sip_core::json::ext::SipMessageJson;
105///
106/// # fn example() -> Option<()> {
107/// let request = RequestBuilder::invite("sip:bob@example.com").unwrap()
108/// .from("Alice", "sip:alice@example.com", Some("tag12345"))
109/// .to("Bob", "sip:bob@example.com", None)
110/// .build();
111///
112/// // Use helper methods for common headers
113/// let from_uri = request.from_uri()?;
114/// let from_tag = request.from_tag()?;
115/// let call_id = request.call_id()?;
116///
117/// println!("Call-ID: {}", call_id);
118/// println!("From URI: {}", from_uri);
119/// println!("From tag: {}", from_tag);
120///
121/// // Methods return None when data isn't present
122/// if let Some(to_tag) = request.to_tag() {
123/// println!("Dialog is established (to_tag present)");
124/// } else {
125/// println!("Dialog not yet established (no to_tag)");
126/// }
127/// # Some(())
128/// # }
129/// ```
130///
131/// ### Converting to/from JSON
132///
133/// ```
134/// use rvoip_sip_core::json::{SipJsonExt, SipValue, SipJsonError};
135/// use rvoip_sip_core::prelude::*;
136/// use rvoip_sip_core::types::sip_request::Request;
137/// use std::error::Error;
138///
139/// # fn example() -> std::result::Result<(), Box<dyn Error>> {
140/// let request = RequestBuilder::invite("sip:bob@example.com").unwrap()
141/// .from("Alice", "sip:alice@example.com", Some("tag12345"))
142/// .build();
143///
144/// // Convert to JSON string with proper error handling
145/// let json_str = match request.to_json_string() {
146/// Ok(s) => s,
147/// Err(e) => return Err(Box::new(e)),
148/// };
149/// println!("JSON representation: {}", json_str);
150///
151/// // Convert to pretty-printed JSON string
152/// let pretty_json = match request.to_json_string_pretty() {
153/// Ok(s) => s,
154/// Err(e) => return Err(Box::new(e)),
155/// };
156/// println!("Pretty JSON:\n{}", pretty_json);
157///
158/// // Create a new request from the JSON string
159/// let new_request = match Request::from_json_str(&json_str) {
160/// Ok(r) => r,
161/// Err(e) => return Err(Box::new(e)),
162/// };
163///
164/// // Convert to a SipValue for direct manipulation
165/// let value = match request.to_sip_value() {
166/// Ok(v) => v,
167/// Err(e) => return Err(Box::new(e)),
168/// };
169///
170/// // Access fields on the SipValue directly
171/// let method = value.get_path("method").and_then(|v| v.as_str());
172/// assert_eq!(method, Some("Invite"));
173/// # Ok(())
174/// # }
175/// ```
176///
177/// ## When to Use Each Approach
178///
179/// - **Typed Headers API**: When type safety is critical (production code)
180/// - **Path Accessors**: For direct, simple access to known fields
181/// - **Query Interface**: For complex searches or exploring message structure
182/// - **SipMessageJson methods**: For common SIP headers with a concise API
183/// - **Direct SipValue manipulation**: For advanced JSON operations
184///
185/// Error type for JSON operations.
186///
187/// This enum represents the various errors that can occur during JSON operations
188/// on SIP messages.
189///
190/// # Examples
191///
192/// ```
193/// use rvoip_sip_core::json::{SipJsonExt, SipJsonError};
194/// use std::error::Error;
195///
196/// // Function that demonstrates SipJsonError handling
197/// fn show_error_handling() {
198/// // Using Result with SipJsonError
199/// let result = "not_valid_json".parse::<serde_json::Value>()
200/// .map_err(|e| SipJsonError::DeserializeError(e));
201///
202/// match result {
203/// Ok(_) => println!("Successfully parsed"),
204/// Err(SipJsonError::DeserializeError(e)) => println!("Deserialization error: {}", e),
205/// Err(e) => println!("Other error: {}", e),
206/// }
207/// }
208/// ```
209#[derive(Debug)]
210pub enum SipJsonError {
211 /// Error during serialization
212 SerializeError(serde_json::Error),
213 /// Error during deserialization
214 DeserializeError(serde_json::Error),
215 /// Invalid path provided
216 InvalidPath(String),
217 /// Invalid query provided
218 InvalidQuery(String),
219 /// Type conversion error
220 TypeConversionError(String),
221 /// Other errors
222 Other(String),
223}
224
225impl fmt::Display for SipJsonError {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 match self {
228 Self::SerializeError(e) => write!(f, "Serialization error: {}", e),
229 Self::DeserializeError(e) => write!(f, "Deserialization error: {}", e),
230 Self::InvalidPath(e) => write!(f, "Invalid path: {}", e),
231 Self::InvalidQuery(e) => write!(f, "Invalid query: {}", e),
232 Self::TypeConversionError(e) => write!(f, "Type conversion error: {}", e),
233 Self::Other(e) => write!(f, "Other error: {}", e),
234 }
235 }
236}
237
238impl Error for SipJsonError {}
239
240/// Result type for JSON operations.
241///
242/// This is a convenience type alias for `Result<T, SipJsonError>`.
243///
244/// # Examples
245///
246/// ```
247/// use rvoip_sip_core::json::{SipJsonResult, SipJsonError, SipValue};
248/// use std::error::Error;
249///
250/// # fn example() -> std::result::Result<(), Box<dyn Error>> {
251/// // Function that returns a SipJsonResult
252/// fn parse_json(input: &str) -> SipJsonResult<SipValue> {
253/// let json_value = serde_json::from_str(input)
254/// .map_err(|e| SipJsonError::DeserializeError(e))?;
255///
256/// Ok(SipValue::from_json_value(&json_value))
257/// }
258///
259/// // Using ? operator with SipJsonResult
260/// let value = parse_json(r#"{"key": "value"}"#).map_err(|e| Box::new(e) as Box<dyn Error>)?;
261/// assert!(value.is_object());
262/// # Ok(())
263/// # }
264/// ```
265pub type SipJsonResult<T> = Result<T, SipJsonError>;
266
267/// Core trait for converting between SIP types and JSON.
268///
269/// This trait provides the fundamental conversion methods between SIP types
270/// and JSON representation. It is implemented automatically for any type that
271/// implements Serialize and DeserializeOwned.
272///
273/// # Examples
274///
275/// Basic usage with a custom type:
276///
277/// ```
278/// # use rvoip_sip_core::json::{SipJson, SipValue};
279/// # use serde::{Serialize, Deserialize};
280/// #[derive(Serialize, Deserialize)]
281/// struct Person {
282/// name: String,
283/// age: u32,
284/// }
285///
286/// # fn example() -> Option<()> {
287/// let person = Person { name: "Alice".to_string(), age: 30 };
288///
289/// // Convert to SipValue
290/// let value = person.to_sip_value().ok()?;
291///
292/// // Convert back
293/// let restored: Person = Person::from_sip_value(&value).ok()?;
294/// assert_eq!(restored.name, "Alice");
295/// assert_eq!(restored.age, 30);
296/// # Some(())
297/// # }
298/// ```
299///
300/// Using with SIP types:
301///
302/// ```
303/// # use rvoip_sip_core::json::{SipJson, SipValue, SipJsonError};
304/// # use rvoip_sip_core::prelude::*;
305/// # use rvoip_sip_core::types::sip_request::Request;
306/// # fn example() -> std::result::Result<(), Box<dyn std::error::Error>> {
307/// // Create a SIP request
308/// let request = RequestBuilder::invite("sip:bob@example.com").unwrap()
309/// .from("Alice", "sip:alice@example.com", Some("tag12345"))
310/// .build();
311///
312/// // Convert to SipValue
313/// let value = <Request as SipJson>::to_sip_value(&request).map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
314///
315/// // Create a modified value
316/// let mut obj = value.as_object().unwrap().clone();
317/// if let Some(method) = obj.get_mut("method") {
318/// *method = SipValue::String("REGISTER".to_string());
319/// }
320/// let modified_value = SipValue::Object(obj);
321///
322/// // Convert back to a SIP request (now with REGISTER method)
323/// let modified_request = <Request as SipJson>::from_sip_value(&modified_value).map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
324/// assert_eq!(modified_request.method().to_string(), "REGISTER");
325/// # Ok(())
326/// # }
327/// ```
328pub trait SipJson {
329 /// Convert this type to a SipValue.
330 ///
331 /// # Returns
332 /// - `Ok(SipValue)` on success
333 /// - `Err(SipJsonError)` on failure
334 ///
335 /// # Examples
336 ///
337 /// ```
338 /// # use rvoip_sip_core::json::{SipJson, SipValue, SipJsonError};
339 /// # use rvoip_sip_core::prelude::*;
340 /// # use rvoip_sip_core::types::sip_request::Request;
341 /// # fn example() -> std::result::Result<(), Box<dyn std::error::Error>> {
342 /// let request = RequestBuilder::invite("sip:bob@example.com").unwrap().build();
343 ///
344 /// // Convert to SipValue
345 /// let value = <Request as SipJson>::to_sip_value(&request).map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
346 ///
347 /// // Now we can access fields directly
348 /// let method = value.get_path("method").and_then(|v| v.as_str());
349 /// let uri = value.get_path("uri");
350 ///
351 /// assert_eq!(method, Some("Invite"));
352 /// assert!(uri.is_some());
353 /// # Ok(())
354 /// # }
355 /// ```
356 fn to_sip_value(&self) -> SipJsonResult<SipValue>;
357
358 /// Create this type from a SipValue.
359 ///
360 /// # Parameters
361 /// - `value`: The SipValue to convert from
362 ///
363 /// # Returns
364 /// - `Ok(Self)` on success
365 /// - `Err(SipJsonError)` on failure
366 ///
367 /// # Examples
368 ///
369 /// ```
370 /// # use rvoip_sip_core::json::{SipJson, SipValue};
371 /// # use rvoip_sip_core::types::sip_request::Request;
372 /// # use serde_json::json;
373 /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
374 /// // Create a SipValue from a json! macro
375 /// let json_value = json!({
376 /// "method": "INVITE",
377 /// "uri": {
378 /// "scheme": "Sip",
379 /// "user": "bob",
380 /// "host": {"Domain": "example.com"}
381 /// },
382 /// "version": "SIP/2.0",
383 /// "headers": []
384 /// });
385 ///
386 /// let sip_value = SipValue::from_json_value(&json_value);
387 ///
388 /// // Convert to a SIP request
389 /// let request = Request::from_sip_value(&sip_value)?;
390 ///
391 /// assert_eq!(request.method().to_string(), "INVITE");
392 /// # Ok(())
393 /// # }
394 /// ```
395 fn from_sip_value(value: &SipValue) -> SipJsonResult<Self> where Self: Sized;
396}
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401 use crate::json::value::SipValue;
402 use crate::json::path;
403 use crate::json::query;
404 use crate::types::sip_request::Request;
405 use crate::builder::SimpleRequestBuilder;
406 use std::collections::HashMap;
407 use serde::{Serialize, Deserialize};
408
409 #[test]
410 fn test_module_exports() {
411 // Verify that the essential types and modules are exported
412 let _: SipValue = SipValue::Null;
413
414 // Test that we can create errors of different types
415 let _: SipJsonError = SipJsonError::Other("test error".to_string());
416 let _: SipJsonError = SipJsonError::InvalidPath("invalid.path".to_string());
417
418 // Test Result type alias
419 let result: SipJsonResult<()> = Ok(());
420 assert!(result.is_ok());
421 }
422
423 #[test]
424 fn test_error_display() {
425 // Test the Display implementation for SipJsonError
426 let err1 = SipJsonError::InvalidPath("test.path".to_string());
427 let err2 = SipJsonError::InvalidQuery("$.invalid".to_string());
428 let err3 = SipJsonError::TypeConversionError("cannot convert".to_string());
429 let err4 = SipJsonError::Other("other error".to_string());
430
431 // Check the error messages
432 assert!(format!("{}", err1).contains("Invalid path"));
433 assert!(format!("{}", err2).contains("Invalid query"));
434 assert!(format!("{}", err3).contains("Type conversion error"));
435 assert!(format!("{}", err4).contains("Other error"));
436
437 // Check Error trait implementation
438 let _: Box<dyn Error> = Box::new(err1);
439 }
440
441 #[test]
442 fn test_serde_errors() {
443 // Test serialization error with invalid serialization
444 #[derive(Serialize)]
445 struct NonSerializable {
446 // This field can't be serialized directly
447 #[serde(skip_serializing)]
448 value: std::cell::RefCell<i32>,
449 }
450
451 let non_serializable = NonSerializable {
452 value: std::cell::RefCell::new(42),
453 };
454
455 // Attempting to convert a SipValue to JSON string with invalid UTF-8
456 let serialization_result = serde_json::to_string(&non_serializable);
457 let error = SipJsonError::SerializeError(serialization_result.err().unwrap_or_else(|| {
458 // If somehow serialization succeeded, create a dummy error
459 serde_json::from_str::<serde_json::Value>("{invalid}").unwrap_err()
460 }));
461 assert!(format!("{}", error).contains("Serialization error"));
462
463 // Test deserialization error with actual parsing
464 let invalid_json = "{invalid json";
465 let result: Result<serde_json::Value, _> = serde_json::from_str(invalid_json);
466 let error = SipJsonError::DeserializeError(result.unwrap_err());
467 assert!(format!("{}", error).contains("Deserialization error"));
468 }
469
470 #[test]
471 fn test_sipjson_implementation() {
472 // Instead of testing the trait implementation, which seems problematic,
473 // test the SipValue conversions directly
474
475 // Create a simple SipValue
476 let mut obj = HashMap::new();
477 obj.insert("name".to_string(), SipValue::String("test".to_string()));
478 obj.insert("value".to_string(), SipValue::Number(42.0));
479 let value = SipValue::Object(obj);
480
481 // Check field access works
482 let name_value = value.get_path("name").unwrap();
483 assert_eq!(name_value.as_str(), Some("test"));
484
485 let value_field = value.get_path("value").unwrap();
486 assert_eq!(value_field.as_i64(), Some(42));
487
488 // Test that we can convert to JSON and back
489 let json_str = value.to_string().unwrap();
490 let parsed = SipValue::from_str(&json_str).unwrap();
491
492 // Check the round-trip conversion preserved values
493 let name_value2 = parsed.get_path("name").unwrap();
494 assert_eq!(name_value2.as_str(), Some("test"));
495
496 let value_field2 = parsed.get_path("value").unwrap();
497 assert_eq!(value_field2.as_i64(), Some(42));
498 }
499
500 #[test]
501 fn test_json_integration() {
502 // Create a SIP request
503 let request = SimpleRequestBuilder::invite("sip:bob@example.com").unwrap()
504 .from("Alice", "sip:alice@example.com", Some("tag12345"))
505 .to("Bob", "sip:bob@example.com", None)
506 .build();
507
508 // Convert to SipValue
509 let value = <Request as SipJson>::to_sip_value(&request).unwrap();
510
511 // Test path access
512 let method_value = path::get_path(&value, "method").unwrap();
513 assert_eq!(method_value.as_str(), Some("Invite"));
514
515 // Test query access
516 let tags = query::query(&value, "$..Tag");
517 assert!(!tags.is_empty());
518
519 // Test simple SipValue manipulation
520 let mut method_str = String::new();
521 if let Some(method) = path::get_path(&value, "method") {
522 if let Some(m) = method.as_str() {
523 method_str = m.to_string();
524 }
525 }
526 assert_eq!(method_str, "Invite");
527
528 // Test direct modification of SipValue
529 let mut obj = HashMap::new();
530 obj.insert("key".to_string(), SipValue::String("value".to_string()));
531 let mut test_value = SipValue::Object(obj);
532
533 // Modify the value
534 if let SipValue::Object(ref mut map) = test_value {
535 map.insert("key".to_string(), SipValue::String("modified".to_string()));
536 }
537
538 // Verify modification
539 if let SipValue::Object(map) = &test_value {
540 if let Some(SipValue::String(s)) = map.get("key") {
541 assert_eq!(s, "modified");
542 } else {
543 panic!("Expected String value for key");
544 }
545 } else {
546 panic!("Expected Object");
547 }
548 }
549
550 #[test]
551 fn test_json_result_operators() {
552 // Test ? operator with chained results
553 fn parse_and_extract(json: &str) -> SipJsonResult<String> {
554 let value = serde_json::from_str::<serde_json::Value>(json)
555 .map_err(|e| SipJsonError::DeserializeError(e))?;
556
557 let sip_value = SipValue::from_json_value(&value);
558
559 // Fix by getting the SipValue first, then calling as_str
560 if let Some(name_value) = sip_value.get_path("name") {
561 if let Some(name) = name_value.as_str() {
562 Ok(name.to_string())
563 } else {
564 Err(SipJsonError::TypeConversionError("name is not a string".to_string()))
565 }
566 } else {
567 Err(SipJsonError::Other("name field not found".to_string()))
568 }
569 }
570
571 // Test success case
572 let result = parse_and_extract(r#"{"name": "Alice"}"#);
573 assert_eq!(result.unwrap(), "Alice");
574
575 // Test failure case
576 let result = parse_and_extract(r#"{"not_name": "Bob"}"#);
577 assert!(result.is_err());
578 assert!(format!("{}", result.unwrap_err()).contains("name field not found"));
579
580 // Test invalid JSON case
581 let result = parse_and_extract(r#"{invalid"#);
582 assert!(result.is_err());
583 assert!(format!("{}", result.unwrap_err()).contains("Deserialization error"));
584 }
585
586 #[test]
587 fn test_error_handling() {
588 // Test invalid path
589 let value = SipValue::Object(HashMap::new());
590 assert!(path::get_path(&value, "invalid.path").is_none());
591
592 // Test invalid query - use a pattern that's guaranteed to not match
593 let results = query::query(&value, "$[?(@.field == 'value that cannot exist')]");
594 assert!(results.is_empty());
595
596 // Test conversion error scenarios
597 #[derive(Serialize, Deserialize)]
598 struct RequiredField {
599 required: String,
600 }
601
602 // Missing required field should fail to deserialize
603 let missing_field = SipValue::Object(HashMap::new());
604 let result = <RequiredField as SipJson>::from_sip_value(&missing_field);
605 assert!(result.is_err());
606
607 // Type mismatch should fail
608 let mut obj = HashMap::new();
609 obj.insert("required".to_string(), SipValue::Number(42.0));
610 let wrong_type = SipValue::Object(obj);
611
612 let result = <RequiredField as SipJson>::from_sip_value(&wrong_type);
613 assert!(result.is_err());
614 }
615}
616
617/// SIP JSON module implementation details.
618///
619/// # Examples
620///
621/// ```
622/// use rvoip_sip_core::json::{SipJsonExt, SipJsonError, SipValue};
623/// use rvoip_sip_core::prelude::*;
624/// use std::error::Error;
625///
626/// # fn example() -> std::result::Result<(), Box<dyn Error>> {
627/// // Create a request
628/// let request = RequestBuilder::invite("sip:bob@example.com").unwrap()
629/// .from("Alice", "sip:alice@example.com", Some("tag12345"))
630/// .build();
631///
632/// // Convert to JSON string with error handling
633/// let json_str = match request.to_json_string() {
634/// Ok(s) => s,
635/// Err(SipJsonError::SerializeError(e)) => return Err(Box::new(e)),
636/// Err(e) => return Err(Box::new(e)),
637/// };
638///
639/// // Parse back with error handling
640/// let parsed_request = match Request::from_json_str(&json_str) {
641/// Ok(req) => req,
642/// Err(SipJsonError::DeserializeError(e)) => return Err(Box::new(e)),
643/// Err(e) => return Err(Box::new(e)),
644/// };
645///
646/// // Convert to SipValue with error handling
647/// let value = match request.to_sip_value() {
648/// Ok(v) => v,
649/// Err(e) => return Err(Box::new(e)),
650/// };
651///
652/// // Access fields using get_path
653/// if let Some(method) = value.get_path("method").and_then(|v| v.as_str()) {
654/// println!("Method: {}", method);
655/// }
656/// # Ok(())
657/// # }
658/// ```
659mod implementaton { /* This is just a placeholder to associate the doc comment */ }