qubit_json/decode/json_decode_error_kind.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//! Defines stable categories for JSON decoding failures.
9
10use std::fmt;
11use std::str::FromStr;
12
13/// Represents the coarse category of a JSON decoding failure.
14///
15/// This exhaustive enum is the stable branching contract shared by strict and
16/// normalizing decoders.
17///
18/// # Examples
19///
20/// ```
21/// use qubit_json::decode::JsonDecodeErrorKind;
22///
23/// let kind = "invalid_json".parse::<JsonDecodeErrorKind>()?;
24/// assert_eq!(kind, JsonDecodeErrorKind::InvalidJson);
25/// # Ok::<(), &'static str>(())
26/// ```
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum JsonDecodeErrorKind {
29 /// Reading bytes from the input failed.
30 Read,
31 /// A configured resource budget rejected a measurement.
32 Budget,
33 /// Normalization produced no JSON document from the supplied input.
34 EmptyInput,
35 /// Raw byte input was not valid UTF-8.
36 InvalidUtf8,
37 /// Input was not one valid JSON document under the numeric contract.
38 InvalidJson,
39 /// A valid document had an unexpected top-level kind.
40 UnexpectedTopLevel,
41 /// A valid admitted document could not deserialize into the target type.
42 Deserialize,
43}
44
45impl fmt::Display for JsonDecodeErrorKind {
46 /// Writes the stable snake-case category name.
47 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
48 formatter.write_str(match self {
49 Self::Read => "read",
50 Self::Budget => "budget",
51 Self::EmptyInput => "empty_input",
52 Self::InvalidUtf8 => "invalid_utf8",
53 Self::InvalidJson => "invalid_json",
54 Self::UnexpectedTopLevel => "unexpected_top_level",
55 Self::Deserialize => "deserialize",
56 })
57 }
58}
59
60impl FromStr for JsonDecodeErrorKind {
61 type Err = &'static str;
62
63 /// Parses a stable category name without ASCII case sensitivity.
64 ///
65 /// # Errors
66 ///
67 /// Returns `"unknown JsonDecodeErrorKind"` when `value` does not match a
68 /// supported category name.
69 fn from_str(value: &str) -> Result<Self, Self::Err> {
70 if value.eq_ignore_ascii_case("read") {
71 Ok(Self::Read)
72 } else if value.eq_ignore_ascii_case("budget") {
73 Ok(Self::Budget)
74 } else if value.eq_ignore_ascii_case("empty_input") {
75 Ok(Self::EmptyInput)
76 } else if value.eq_ignore_ascii_case("invalid_utf8") {
77 Ok(Self::InvalidUtf8)
78 } else if value.eq_ignore_ascii_case("invalid_json") {
79 Ok(Self::InvalidJson)
80 } else if value.eq_ignore_ascii_case("unexpected_top_level") {
81 Ok(Self::UnexpectedTopLevel)
82 } else if value.eq_ignore_ascii_case("deserialize") {
83 Ok(Self::Deserialize)
84 } else {
85 Err("unknown JsonDecodeErrorKind")
86 }
87 }
88}