• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

qubit-ltd / rs-json / 7edb441c-ea7f-46fe-8505-fdcc29bb576c

16 Apr 2026 04:23PM UTC coverage: 98.677% (+0.2%) from 98.513%
7edb441c-ea7f-46fe-8505-fdcc29bb576c

push

circleci

Haixing-Hu
chore: adopt Rust 2024, enable doctests, harden llvm-cov in CI

373 of 378 relevant lines covered (98.68%)

9062.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.82
/src/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

13
use std::fmt;
14

15
use 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)]
22
pub 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

61
impl 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 {
2✔
66
        Self {
2✔
67
            kind: JsonDecodeErrorKind::InputTooLarge,
2✔
68
            stage: JsonDecodeStage::Normalize,
2✔
69
            message: format!(
2✔
70
                "JSON input is too large: {} bytes exceed configured limit {} bytes",
2✔
71
                actual_bytes, max_bytes
2✔
72
            ),
2✔
73
            expected_top_level: None,
2✔
74
            actual_top_level: None,
2✔
75
            line: None,
2✔
76
            column: None,
2✔
77
            input_bytes: Some(actual_bytes),
2✔
78
            max_input_bytes: Some(max_bytes),
2✔
79
        }
2✔
80
    }
2✔
81

82
    /// Creates an error indicating that the input became empty after
83
    /// normalization.
84
    #[inline]
85
    pub(crate) fn empty_input() -> Self {
7✔
86
        Self {
7✔
87
            kind: JsonDecodeErrorKind::EmptyInput,
7✔
88
            stage: JsonDecodeStage::Normalize,
7✔
89
            message: "JSON input is empty after normalization".to_string(),
7✔
90
            expected_top_level: None,
7✔
91
            actual_top_level: None,
7✔
92
            line: None,
7✔
93
            column: None,
7✔
94
            input_bytes: None,
7✔
95
            max_input_bytes: None,
7✔
96
        }
7✔
97
    }
7✔
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 {
9,006✔
103
        let line = error.line();
9,006✔
104
        let column = error.column();
9,006✔
105
        Self {
9,006✔
106
            kind: JsonDecodeErrorKind::InvalidJson,
9,006✔
107
            stage: JsonDecodeStage::Parse,
9,006✔
108
            message: format!("Failed to parse JSON: {error}"),
9,006✔
109
            expected_top_level: None,
9,006✔
110
            actual_top_level: None,
9,006✔
111
            line: (line > 0).then_some(line),
9,006✔
112
            column: (column > 0).then_some(column),
9,006✔
113
            input_bytes,
9,006✔
114
            max_input_bytes: None,
9,006✔
115
        }
9,006✔
116
    }
9,006✔
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(
4✔
122
        expected: JsonTopLevelKind,
4✔
123
        actual: JsonTopLevelKind,
4✔
124
    ) -> Self {
4✔
125
        Self {
4✔
126
            kind: JsonDecodeErrorKind::UnexpectedTopLevel,
4✔
127
            stage: JsonDecodeStage::TopLevelCheck,
4✔
128
            message: format!("Unexpected JSON top-level type: expected {expected}, got {actual}"),
4✔
129
            expected_top_level: Some(expected),
4✔
130
            actual_top_level: Some(actual),
4✔
131
            line: None,
4✔
132
            column: None,
4✔
133
            input_bytes: None,
4✔
134
            max_input_bytes: None,
4✔
135
        }
4✔
136
    }
4✔
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 {
2✔
142
        let line = error.line();
2✔
143
        let column = error.column();
2✔
144
        Self {
2✔
145
            kind: JsonDecodeErrorKind::Deserialize,
2✔
146
            stage: JsonDecodeStage::Deserialize,
2✔
147
            message: format!("Failed to deserialize JSON value: {error}"),
2✔
148
            expected_top_level: None,
2✔
149
            actual_top_level: None,
2✔
150
            line: (line > 0).then_some(line),
2✔
151
            column: (column > 0).then_some(column),
2✔
152
            input_bytes,
2✔
153
            max_input_bytes: None,
2✔
154
        }
2✔
155
    }
2✔
156
}
157

158
impl fmt::Display for JsonDecodeError {
159
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7✔
160
        match self.kind {
7✔
161
            JsonDecodeErrorKind::InputTooLarge => f.write_str(&self.message),
2✔
162
            JsonDecodeErrorKind::EmptyInput => f.write_str(&self.message),
1✔
163
            JsonDecodeErrorKind::UnexpectedTopLevel => f.write_str(&self.message),
1✔
164
            JsonDecodeErrorKind::InvalidJson | JsonDecodeErrorKind::Deserialize => {
165
                match (self.line, self.column) {
3✔
166
                    (Some(line), Some(column)) => {
3✔
167
                        write!(f, "{} (line {}, column {})", self.message, line, column)
3✔
168
                    }
169
                    _ => f.write_str(&self.message),
×
170
                }
171
            }
172
        }
173
    }
7✔
174
}
175

176
impl std::error::Error for JsonDecodeError {}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc