Skip to main content

qubit_json/decode/
jcs_decode_error.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Error returned by the JCS JSON text decoder.
9
10use std::error::Error;
11use std::fmt;
12
13use qubit_budget::ResourceQuantity;
14use qubit_budget::json::JsonResource;
15
16use super::JsonDecodeError;
17
18/// JCS decoding failure with explicit duplicate-key handling.
19///
20/// # Type Parameters
21///
22/// * `R` - Resource identity used by the wrapped budget error.
23/// * `Q` - Quantity representation used for resource accounting.
24///
25/// # Examples
26///
27/// ```
28/// use qubit_json::decode::{JcsDecodeError, JcsDecoder};
29///
30/// let error = JcsDecoder::unlimited()
31///     .decode_str(r#"{"x":1,"x":2}"#)
32///     .expect_err("duplicate keys must be rejected");
33/// assert!(matches!(error, JcsDecodeError::DuplicateKey { .. }));
34/// ```
35#[must_use]
36#[derive(Debug, Clone)]
37pub enum JcsDecodeError<R = JsonResource, Q = usize>
38where
39    Q: Copy + fmt::Debug,
40{
41    /// An input, UTF-8, budget, or lexical admission failure.
42    Decode(
43        /// Shared decode error with stable category and input diagnostics.
44        JsonDecodeError<R, Q>,
45    ),
46    /// An object contains two keys equal after JSON escape decoding.
47    DuplicateKey {
48        /// One-based source line of the second key token.
49        line: usize,
50        /// One-based UTF-8 character column of the second key token.
51        column: usize,
52    },
53    /// Historical compatibility variant; the shared builder no longer emits it.
54    MaterializationDepthLimit {
55        /// Maximum number of nested containers accepted during construction.
56        limit: usize,
57        /// One-based source line where the first over-limit container begins.
58        line: usize,
59        /// One-based UTF-8 character column of that container token.
60        column: usize,
61    },
62}
63
64impl<R, Q> fmt::Display for JcsDecodeError<R, Q>
65where
66    R: fmt::Debug,
67    Q: ResourceQuantity,
68{
69    /// Formats the decoding failure with its source location when applicable.
70    ///
71    /// # Parameters
72    ///
73    /// * `formatter` - Destination that receives the formatted message.
74    ///
75    /// # Returns
76    ///
77    /// `Ok(())` after the message is written.
78    ///
79    /// # Errors
80    ///
81    /// Returns the formatter error if writing the message fails.
82    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
83        match self {
84            Self::Decode(error) => error.fmt(formatter),
85            Self::DuplicateKey { line, column } => {
86                write!(formatter, "duplicate JSON object key at line {line}, column {column}")
87            }
88            Self::MaterializationDepthLimit { limit, line, column } => write!(
89                formatter,
90                "JCS value construction depth exceeds {limit} at line {line}, column {column}"
91            ),
92        }
93    }
94}
95
96impl<R, Q> Error for JcsDecodeError<R, Q>
97where
98    R: fmt::Debug + 'static,
99    Q: ResourceQuantity + 'static,
100{
101    /// Returns the wrapped decoding error for the standard error chain.
102    ///
103    /// # Returns
104    ///
105    /// The wrapped decode error for `Decode`, or `None` for the JCS-specific
106    /// duplicate-key and construction-depth failures.
107    fn source(&self) -> Option<&(dyn Error + 'static)> {
108        match self {
109            Self::Decode(error) => Some(error),
110            Self::DuplicateKey { .. } | Self::MaterializationDepthLimit { .. } => None,
111        }
112    }
113}
114
115impl<R, Q> From<JsonDecodeError<R, Q>> for JcsDecodeError<R, Q>
116where
117    Q: Copy + fmt::Debug,
118{
119    /// Wraps a regular decoding failure as a JCS decoding failure.
120    ///
121    /// # Parameters
122    ///
123    /// * `error` - Regular decoding failure to wrap.
124    ///
125    /// # Returns
126    ///
127    /// A JCS error containing the supplied decoding failure.
128    #[inline]
129    fn from(error: JsonDecodeError<R, Q>) -> Self {
130        Self::Decode(error)
131    }
132}