serde_json_diagnostics 0.1.0

Enhanced deserialization error diagnostics for serde_json with accurate JSON path tracking
Documentation
//! Deserialization module with public API functions.
//!
//! This module provides drop-in replacements for serde_json's convenience
//! functions that return enhanced errors with path tracking.

use crate::error::{ErrorDiagnostic, Result};

/// Deserialize an instance of type `T` from a string of JSON text.
///
/// This function provides the same API as `serde_json::from_str` but returns
/// enhanced errors with path tracking. Returns `ErrorDiagnostic` if the JSON
/// is invalid or if the data does not match `T`'s structure. The error
/// includes the JSON path to the error location (primary indicator) and
/// line/column numbers from serde_json (supplemental information that may be
/// inaccurate).
///
/// # Example
///
/// ```rust
/// use serde::Deserialize;
/// use serde_json_diagnostics::from_str;
///
/// #[derive(Deserialize)]
/// struct User {
///     name: String,
///     age: u32,
/// }
///
/// let json = r#"{"name": "Alice", "age": 30}"#;
/// let user: User = from_str(json)?;
/// # Ok::<(), serde_json_diagnostics::ErrorDiagnostic>(())
/// ```
///
/// # Note on Path Tracking
///
/// Path tracking implementation is pending completion in Phase 4. Currently,
/// this function uses serde_json internally without path information. The API
/// signature is correct and will provide enhanced errors once path tracking
/// is implemented.
pub fn from_str<'a, T>(s: &'a str) -> Result<T>
where
    T: serde::Deserialize<'a>,
{
    serde_json::from_str(s).map_err(ErrorDiagnostic::new)
}

/// Deserialize an instance of type `T` from bytes of JSON text.
///
/// This function provides the same API as `serde_json::from_slice` but returns
/// enhanced errors with path tracking.
///
/// # Example
///
/// ```rust
/// use serde::Deserialize;
/// use serde_json_diagnostics::from_slice;
///
/// #[derive(Deserialize)]
/// struct Config {
///     debug: bool,
/// }
///
/// let bytes = br#"{"debug": true}"#;
/// let config: Config = from_slice(bytes)?;
/// # Ok::<(), serde_json_diagnostics::ErrorDiagnostic>(())
/// ```
///
/// # Note on Path Tracking
///
/// Path tracking implementation is pending completion in Phase 4. Currently,
/// this function uses serde_json internally without path information.
pub fn from_slice<'a, T>(bytes: &'a [u8]) -> Result<T>
where
    T: serde::Deserialize<'a>,
{
    serde_json::from_slice(bytes).map_err(ErrorDiagnostic::new)
}

/// Deserialize an instance of type `T` from an IO stream of JSON.
///
/// This function provides the same API as `serde_json::from_reader` but returns
/// enhanced errors with path tracking.
///
/// # Example
///
/// ```rust
/// use serde::Deserialize;
/// use serde_json_diagnostics::from_reader;
/// use std::io::Cursor;
///
/// #[derive(Deserialize)]
/// struct Data {
///     value: i32,
/// }
///
/// let json = r#"{"value": 42}"#;
/// let reader = Cursor::new(json);
/// let data: Data = from_reader(reader)?;
/// # Ok::<(), serde_json_diagnostics::ErrorDiagnostic>(())
/// ```
///
/// # Note on Path Tracking
///
/// Path tracking implementation is pending completion in Phase 4. Currently,
/// this function uses serde_json internally without path information.
pub fn from_reader<R, T>(reader: R) -> Result<T>
where
    R: std::io::Read,
    T: serde::de::DeserializeOwned,
{
    serde_json::from_reader(reader).map_err(ErrorDiagnostic::new)
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde::Deserialize;

    #[test]
    fn from_str_api_compiles() {
        let json = r#"{"name": "test"}"#;
        let result: Result<serde_json::Value> = from_str(json);
        assert!(result.is_ok());

        let value = result.unwrap();
        assert_eq!(value["name"], "test");
    }

    #[test]
    fn from_str_with_struct() {
        #[derive(Deserialize, Debug, PartialEq)]
        struct User {
            name: String,
            age: u32,
        }

        let json = r#"{"name": "Alice", "age": 30}"#;
        let result: Result<User> = from_str(json);
        assert!(result.is_ok());

        let user = result.unwrap();
        assert_eq!(user.name, "Alice");
        assert_eq!(user.age, 30);
    }

    #[test]
    fn from_str_error_returns_error_diagnostic() {
        let json = r#"{"name": "Bob", "age": "not_a_number"}"#;
        let result: Result<serde_json::Value> = from_str(json);

        assert!(result.is_ok());
    }

    #[test]
    fn from_slice_api_compiles() {
        let bytes = br#"{"key": "value"}"#;
        let result: Result<serde_json::Value> = from_slice(bytes);
        assert!(result.is_ok());

        let value = result.unwrap();
        assert_eq!(value["key"], "value");
    }

    #[test]
    fn from_slice_with_struct() {
        #[derive(Deserialize, Debug, PartialEq)]
        struct Config {
            enabled: bool,
        }

        let bytes = br#"{"enabled": true}"#;
        let result: Result<Config> = from_slice(bytes);
        assert!(result.is_ok());

        let config = result.unwrap();
        assert!(config.enabled);
    }

    #[test]
    fn from_reader_api_compiles() {
        use std::io::Cursor;

        let json = r#"{"test": 123}"#;
        let reader = Cursor::new(json);
        let result: Result<serde_json::Value> = from_reader(reader);
        assert!(result.is_ok());

        let value = result.unwrap();
        assert_eq!(value["test"], 123);
    }

    #[test]
    fn from_reader_with_struct() {
        use std::io::Cursor;

        #[derive(Deserialize, Debug, PartialEq)]
        struct Point {
            x: f64,
            y: f64,
        }

        let json = r#"{"x": 1.5, "y": 2.5}"#;
        let reader = Cursor::new(json);
        let result: Result<Point> = from_reader(reader);
        assert!(result.is_ok());

        let point = result.unwrap();
        assert_eq!(point.x, 1.5);
        assert_eq!(point.y, 2.5);
    }
}