Skip to main content

qubit_json/
json_decode_error.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! Defines the [`JsonDecodeError`] type used by the public decoder API.
10//!
11//! Author: Haixing Hu
12
13use std::fmt;
14
15use crate::{JsonDecodeErrorKind, JsonTopLevelKind};
16
17/// Error returned when lenient JSON decoding fails.
18///
19/// This value captures both a stable category in [`JsonDecodeErrorKind`] and
20/// human-readable context that can be logged or surfaced to the caller.
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct JsonDecodeError {
23    /// Identifies the stable category of this decoding failure.
24    ///
25    /// Callers should match on this field when they need programmatic handling
26    /// that is independent from localized or parser-specific text.
27    pub kind: JsonDecodeErrorKind,
28    /// Stores a human-readable summary of the decoding failure.
29    ///
30    /// The message is intended for diagnostics and normally includes the
31    /// relevant parsing or deserialization context.
32    pub message: String,
33    /// Stores the top-level JSON kind required by the caller, when applicable.
34    ///
35    /// This field is only populated for errors raised by constrained decoding
36    /// methods such as `decode_object()` and `decode_array()`.
37    pub expected_top_level: Option<JsonTopLevelKind>,
38    /// Stores the top-level JSON kind that was actually parsed, when known.
39    ///
40    /// This field is only populated together with `expected_top_level` for
41    /// top-level contract mismatches.
42    pub actual_top_level: Option<JsonTopLevelKind>,
43    /// Stores the one-based line reported by `serde_json`, when available.
44    ///
45    /// This field is primarily useful for invalid JSON syntax and
46    /// deserialization failures that can be mapped back to a parser location.
47    pub line: Option<usize>,
48    /// Stores the one-based column reported by `serde_json`, when available.
49    ///
50    /// Like `line`, this field is only populated when the lower-level parser
51    /// or deserializer reports a concrete source position.
52    pub column: Option<usize>,
53}
54
55impl JsonDecodeError {
56    /// Creates an error indicating that the input became empty after
57    /// normalization.
58    #[inline]
59    pub(crate) fn empty_input() -> Self {
60        Self {
61            kind: JsonDecodeErrorKind::EmptyInput,
62            message: "JSON input is empty after normalization".to_string(),
63            expected_top_level: None,
64            actual_top_level: None,
65            line: None,
66            column: None,
67        }
68    }
69
70    /// Creates an error describing invalid JSON syntax reported by
71    /// `serde_json`.
72    #[inline]
73    pub(crate) fn invalid_json(error: serde_json::Error) -> Self {
74        let line = error.line();
75        let column = error.column();
76        Self {
77            kind: JsonDecodeErrorKind::InvalidJson,
78            message: format!("Failed to parse JSON: {error}"),
79            expected_top_level: None,
80            actual_top_level: None,
81            line: (line > 0).then_some(line),
82            column: (column > 0).then_some(column),
83        }
84    }
85
86    /// Creates an error describing a mismatch between expected and actual
87    /// top-level JSON kinds.
88    #[inline]
89    pub(crate) fn unexpected_top_level(
90        expected: JsonTopLevelKind,
91        actual: JsonTopLevelKind,
92    ) -> Self {
93        Self {
94            kind: JsonDecodeErrorKind::UnexpectedTopLevel,
95            message: format!("Unexpected JSON top-level type: expected {expected}, got {actual}"),
96            expected_top_level: Some(expected),
97            actual_top_level: Some(actual),
98            line: None,
99            column: None,
100        }
101    }
102
103    /// Creates an error describing a type deserialization failure reported by
104    /// `serde_json`.
105    #[inline]
106    pub(crate) fn deserialize(error: serde_json::Error) -> Self {
107        let line = error.line();
108        let column = error.column();
109        Self {
110            kind: JsonDecodeErrorKind::Deserialize,
111            message: format!("Failed to deserialize JSON value: {error}"),
112            expected_top_level: None,
113            actual_top_level: None,
114            line: (line > 0).then_some(line),
115            column: (column > 0).then_some(column),
116        }
117    }
118}
119
120impl fmt::Display for JsonDecodeError {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        match self.kind {
123            JsonDecodeErrorKind::EmptyInput => f.write_str(&self.message),
124            JsonDecodeErrorKind::UnexpectedTopLevel => f.write_str(&self.message),
125            JsonDecodeErrorKind::InvalidJson | JsonDecodeErrorKind::Deserialize => {
126                match (self.line, self.column) {
127                    (Some(line), Some(column)) => {
128                        write!(f, "{} (line {}, column {})", self.message, line, column)
129                    }
130                    _ => f.write_str(&self.message),
131                }
132            }
133        }
134    }
135}
136
137impl std::error::Error for JsonDecodeError {}