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}