Skip to main content

qubit_json/error/
json_decode_error_kind.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Defines the stable error categories returned by the decoder.
9
10use std::{
11    fmt,
12    str::FromStr,
13};
14
15/// Represents the coarse category of a lenient JSON decoding failure.
16///
17/// This type is intended for callers that need stable, programmatic branching
18/// without depending on full error messages produced by lower-level parsers.
19///
20/// # Examples
21///
22/// ```compile_fail
23/// #![deny(unused_must_use)]
24/// use qubit_json::JsonDecodeErrorKind;
25///
26/// fn error_kind() -> JsonDecodeErrorKind {
27///     JsonDecodeErrorKind::InvalidJson
28/// }
29///
30/// error_kind();
31/// ```
32#[must_use]
33#[non_exhaustive]
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub enum JsonDecodeErrorKind {
36    /// Indicates that raw or normalized input size exceeds a configured
37    /// maximum.
38    InputTooLarge,
39    /// Indicates that the input became empty after normalization.
40    EmptyInput,
41    /// Indicates that the raw byte input is not valid UTF-8 text.
42    InvalidUtf8,
43    /// Indicates that the normalized text is not valid JSON syntax.
44    InvalidJson,
45    /// Indicates that the parsed top-level JSON kind is not the one required
46    /// by the decoding method.
47    UnexpectedTopLevel,
48    /// Indicates that the JSON syntax is valid but the value cannot be
49    /// deserialized into the requested Rust type.
50    Deserialize,
51}
52
53impl fmt::Display for JsonDecodeErrorKind {
54    /// Writes the stable snake-case name of this error category.
55    ///
56    /// # Parameters
57    ///
58    /// * `f` - Destination formatter.
59    ///
60    /// # Returns
61    ///
62    /// `Ok(())` when the category name is written successfully.
63    ///
64    /// # Errors
65    ///
66    /// Returns a formatting error when the destination rejects the write.
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        let name = match self {
69            Self::InputTooLarge => "input_too_large",
70            Self::EmptyInput => "empty_input",
71            Self::InvalidUtf8 => "invalid_utf8",
72            Self::InvalidJson => "invalid_json",
73            Self::UnexpectedTopLevel => "unexpected_top_level",
74            Self::Deserialize => "deserialize",
75        };
76        f.write_str(name)
77    }
78}
79
80impl FromStr for JsonDecodeErrorKind {
81    type Err = &'static str;
82
83    /// Parses a stable snake-case error category without ASCII case
84    /// sensitivity.
85    ///
86    /// # Parameters
87    ///
88    /// * `value` - Category name to parse.
89    ///
90    /// # Returns
91    ///
92    /// The matching error category.
93    ///
94    /// # Errors
95    ///
96    /// Returns a static diagnostic when `value` is not a known category name.
97    fn from_str(value: &str) -> Result<Self, Self::Err> {
98        if value.eq_ignore_ascii_case("input_too_large") {
99            Ok(Self::InputTooLarge)
100        } else if value.eq_ignore_ascii_case("empty_input") {
101            Ok(Self::EmptyInput)
102        } else if value.eq_ignore_ascii_case("invalid_utf8") {
103            Ok(Self::InvalidUtf8)
104        } else if value.eq_ignore_ascii_case("invalid_json") {
105            Ok(Self::InvalidJson)
106        } else if value.eq_ignore_ascii_case("unexpected_top_level") {
107            Ok(Self::UnexpectedTopLevel)
108        } else if value.eq_ignore_ascii_case("deserialize") {
109            Ok(Self::Deserialize)
110        } else {
111            Err("unknown JsonDecodeErrorKind")
112        }
113    }
114}