cdumay_json/
lib.rs

1//! [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue)](./LICENSE)
2//! [![cdumay_json on crates.io](https://img.shields.io/crates/v/cdumay_json)](https://crates.io/crates/cdumay_json)
3//! [![cdumay_json on docs.rs](https://docs.rs/cdumay_json/badge.svg)](https://docs.rs/cdumay_json)
4//! [![Source Code Repository](https://img.shields.io/badge/Code-On%20GitHub-blue?logo=GitHub)](https://github.com/cdumay/cdumay_json)
5//!
6//! A small crate to manipulate Json data.
7//!
8//! ## Features
9//!
10//! - Categorizes `serde_json::Error` into specific error types (`Syntax`, `IO`, `Data`, `EOF`)
11//! - Each error type is associated with a custom code, HTTP status, and descriptive message
12//! - Structured output for APIs, logging systems, and observability platforms
13//! - Includes context metadata via `BTreeMap`
14//! - Provides a convenient `convert_json_result!` macro for error conversion
15//!
16//! ## Usage
17//!
18//! Using the `JsonErrorConverter` directly:
19//! ```rust
20//! use cdumay_core::{Error, ErrorConverter};
21//! use serde_json::Value;
22//! use std::collections::BTreeMap;
23//! use cdumay_json::JsonErrorConverter;
24//!
25//! fn parse_json(input: &str) -> cdumay_core::Result<Value> {
26//!     serde_json::from_str::<Value>(input).map_err(|e| {
27//!        let mut ctx = BTreeMap::new();
28//!        ctx.insert("input".to_string(), serde_value::Value::String(input.to_string()));
29//!        JsonErrorConverter::convert(&e, "Failed to parse JSON".to_string(), ctx)
30//!    })
31//! }
32//! ```
33//!
34//! Using the `convert_json_result!` macro:
35//! ```rust
36//! use cdumay_json::convert_json_result;
37//! use serde_json::Value;
38//! use std::collections::BTreeMap;
39//! use cdumay_core::{Error, ErrorConverter};
40//!
41//! fn parse_json(input: &str) -> cdumay_core::Result<Value> {
42//!     // Basic usage with just the result
43//!     convert_json_result!(serde_json::from_str::<Value>(input));
44//!
45//!     // With custom context
46//!     let mut ctx = BTreeMap::new();
47//!     ctx.insert("input".to_string(), serde_value::Value::String(input.to_string()));
48//!     convert_json_result!(serde_json::from_str::<Value>(input), ctx.clone());
49//!
50//!     // With custom context and message
51//!     convert_json_result!(serde_json::from_str::<Value>(input), ctx, "Failed to parse JSON")
52//! }
53//! ```
54#[macro_use]
55mod macros;
56
57use cdumay_core::{Error, ErrorConverter, define_errors, define_kinds};
58use serde_json::error::Category;
59use std::collections::BTreeMap;
60
61define_kinds! {
62    JsonSyntax = (400, "Syntax Error"),
63    JsonData = (400, "Invalid JSON data"),
64    JsonEof = (500, "Reached the end of the input data"),
65    JsonIo = (500, "IO Error"),
66}
67
68define_errors! {
69    IoError = JsonIo,
70    SyntaxError = JsonSyntax,
71    DataError = JsonData,
72    EofError = JsonEof
73}
74
75/// A utility struct for handling JSON errors and converting them into standardized error types.
76pub struct JsonErrorConverter;
77
78impl ErrorConverter for JsonErrorConverter {
79    type Error = serde_json::Error;
80    /// Converts a `serde_json::Error` into a standardized `Error` type based on its category.
81    ///
82    /// # Arguments
83    ///
84    /// * `err` - The `serde_json::Error` to be converted.
85    /// * `text` - A descriptive message for the error.
86    /// * `context` - A mutable reference to a `BTreeMap` containing additional error details.
87    ///
88    /// # Returns
89    ///
90    /// A standardized `Error` instance corresponding to the category of the provided `serde_json::Error`.
91    fn convert(err: &serde_json::Error, text: String, context: BTreeMap<String, serde_value::Value>) -> Error {
92        match err.classify() {
93            Category::Io => IoError::new().with_message(text).with_details(context).into(),
94            Category::Syntax => SyntaxError::new().with_message(text).with_details(context).into(),
95            Category::Data => DataError::new().with_message(text).with_details(context).into(),
96            Category::Eof => EofError::new().with_message(text).with_details(context).into(),
97        }
98    }
99}