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, JsonDecodeStage, 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    /// Identifies which decode stage produced this error.
29    pub stage: JsonDecodeStage,
30    /// Stores a human-readable summary of the decoding failure.
31    ///
32    /// The message is intended for diagnostics and normally includes the
33    /// relevant parsing or deserialization context.
34    pub message: String,
35    /// Stores the top-level JSON kind required by the caller, when applicable.
36    ///
37    /// This field is only populated for errors raised by constrained decoding
38    /// methods such as `decode_object()` and `decode_array()`.
39    pub expected_top_level: Option<JsonTopLevelKind>,
40    /// Stores the top-level JSON kind that was actually parsed, when known.
41    ///
42    /// This field is only populated together with `expected_top_level` for
43    /// top-level contract mismatches.
44    pub actual_top_level: Option<JsonTopLevelKind>,
45    /// Stores the one-based line reported by `serde_json`, when available.
46    ///
47    /// This field is primarily useful for invalid JSON syntax and
48    /// deserialization failures that can be mapped back to a parser location.
49    pub line: Option<usize>,
50    /// Stores the one-based column reported by `serde_json`, when available.
51    ///
52    /// Like `line`, this field is only populated when the lower-level parser
53    /// or deserializer reports a concrete source position.
54    pub column: Option<usize>,
55    /// Stores the input byte length associated with the failure, when known.
56    pub input_bytes: Option<usize>,
57    /// Stores the configured maximum input byte length, when relevant.
58    pub max_input_bytes: Option<usize>,
59}
60
61impl JsonDecodeError {
62    /// Creates an error indicating that the raw input size exceeds a
63    /// configured upper bound.
64    #[inline]
65    pub(crate) fn input_too_large(actual_bytes: usize, max_bytes: usize) -> Self {
66        Self {
67            kind: JsonDecodeErrorKind::InputTooLarge,
68            stage: JsonDecodeStage::Normalize,
69            message: format!(
70                "JSON input is too large: {} bytes exceed configured limit {} bytes",
71                actual_bytes, max_bytes
72            ),
73            expected_top_level: None,
74            actual_top_level: None,
75            line: None,
76            column: None,
77            input_bytes: Some(actual_bytes),
78            max_input_bytes: Some(max_bytes),
79        }
80    }
81
82    /// Creates an error indicating that the input became empty after
83    /// normalization.
84    #[inline]
85    pub(crate) fn empty_input() -> Self {
86        Self {
87            kind: JsonDecodeErrorKind::EmptyInput,
88            stage: JsonDecodeStage::Normalize,
89            message: "JSON input is empty after normalization".to_string(),
90            expected_top_level: None,
91            actual_top_level: None,
92            line: None,
93            column: None,
94            input_bytes: None,
95            max_input_bytes: None,
96        }
97    }
98
99    /// Creates an error describing invalid JSON syntax reported by
100    /// `serde_json`.
101    #[inline]
102    pub(crate) fn invalid_json(error: serde_json::Error, input_bytes: Option<usize>) -> Self {
103        let line = error.line();
104        let column = error.column();
105        Self {
106            kind: JsonDecodeErrorKind::InvalidJson,
107            stage: JsonDecodeStage::Parse,
108            message: format!("Failed to parse JSON: {error}"),
109            expected_top_level: None,
110            actual_top_level: None,
111            line: (line > 0).then_some(line),
112            column: (column > 0).then_some(column),
113            input_bytes,
114            max_input_bytes: None,
115        }
116    }
117
118    /// Creates an error describing a mismatch between expected and actual
119    /// top-level JSON kinds.
120    #[inline]
121    pub(crate) fn unexpected_top_level(
122        expected: JsonTopLevelKind,
123        actual: JsonTopLevelKind,
124    ) -> Self {
125        Self {
126            kind: JsonDecodeErrorKind::UnexpectedTopLevel,
127            stage: JsonDecodeStage::TopLevelCheck,
128            message: format!("Unexpected JSON top-level type: expected {expected}, got {actual}"),
129            expected_top_level: Some(expected),
130            actual_top_level: Some(actual),
131            line: None,
132            column: None,
133            input_bytes: None,
134            max_input_bytes: None,
135        }
136    }
137
138    /// Creates an error describing a type deserialization failure reported by
139    /// `serde_json`.
140    #[inline]
141    pub(crate) fn deserialize(error: serde_json::Error, input_bytes: Option<usize>) -> Self {
142        let line = error.line();
143        let column = error.column();
144        Self {
145            kind: JsonDecodeErrorKind::Deserialize,
146            stage: JsonDecodeStage::Deserialize,
147            message: format!("Failed to deserialize JSON value: {error}"),
148            expected_top_level: None,
149            actual_top_level: None,
150            line: (line > 0).then_some(line),
151            column: (column > 0).then_some(column),
152            input_bytes,
153            max_input_bytes: None,
154        }
155    }
156}
157
158impl fmt::Display for JsonDecodeError {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        match self.kind {
161            JsonDecodeErrorKind::InputTooLarge => f.write_str(&self.message),
162            JsonDecodeErrorKind::EmptyInput => f.write_str(&self.message),
163            JsonDecodeErrorKind::UnexpectedTopLevel => f.write_str(&self.message),
164            JsonDecodeErrorKind::InvalidJson | JsonDecodeErrorKind::Deserialize => {
165                match (self.line, self.column) {
166                    (Some(line), Some(column)) => {
167                        write!(f, "{} (line {}, column {})", self.message, line, column)
168                    }
169                    _ => f.write_str(&self.message),
170                }
171            }
172        }
173    }
174}
175
176impl std::error::Error for JsonDecodeError {}