qubit_json/json_top_level_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 coarse top-level JSON kinds used by constrained decode methods.
9
10use std::{
11 fmt,
12 str::FromStr,
13};
14
15use serde_json::Value;
16
17/// Represents the top-level kind of a parsed JSON value.
18///
19/// The decoder uses this type to report whether the parsed value is an object,
20/// an array, or any other scalar-like JSON value.
21///
22/// # Examples
23///
24/// ```compile_fail
25/// #![deny(unused_must_use)]
26/// use qubit_json::JsonTopLevelKind;
27///
28/// fn top_level_kind() -> JsonTopLevelKind {
29/// JsonTopLevelKind::Other
30/// }
31///
32/// top_level_kind();
33/// ```
34#[must_use]
35#[non_exhaustive]
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub enum JsonTopLevelKind {
38 /// Indicates that the parsed top-level value is a JSON object.
39 Object,
40 /// Indicates that the parsed top-level value is a JSON array.
41 Array,
42 /// Indicates that the parsed top-level value is neither an object nor an
43 /// array.
44 Other,
45}
46
47impl JsonTopLevelKind {
48 /// Classifies the top-level kind of `value`.
49 ///
50 /// This helper is used internally by constrained decode methods and may
51 /// also be useful to callers inspecting decoded [`Value`] instances.
52 ///
53 /// # Parameters
54 ///
55 /// * `value` - JSON value to classify.
56 ///
57 /// # Returns
58 ///
59 /// [`Self::Object`] for objects, [`Self::Array`] for arrays, and
60 /// [`Self::Other`] for scalar-like values.
61 #[inline]
62 pub fn of(value: &Value) -> Self {
63 match value {
64 Value::Object(_) => Self::Object,
65 Value::Array(_) => Self::Array,
66 _ => Self::Other,
67 }
68 }
69
70 /// Provisionally classifies normalized JSON text by its first JSON token.
71 ///
72 /// This helper does not validate JSON syntax. The decoder uses the result
73 /// only as a pre-parse top-level-kind hint.
74 ///
75 /// # Parameters
76 ///
77 /// * `value` - Normalized JSON text to inspect.
78 ///
79 /// # Returns
80 ///
81 /// [`Self::Object`] when the first token is `{`, [`Self::Array`] when it is
82 /// `[`, and [`Self::Other`] otherwise.
83 pub(crate) fn of_normalized_json(value: &str) -> Self {
84 match value
85 .bytes()
86 .find(|byte| !matches!(*byte, b' ' | b'\n' | b'\r' | b'\t'))
87 {
88 Some(b'{') => Self::Object,
89 Some(b'[') => Self::Array,
90 _ => Self::Other,
91 }
92 }
93}
94
95impl From<&Value> for JsonTopLevelKind {
96 /// Classifies a borrowed dynamic JSON value.
97 ///
98 /// # Parameters
99 ///
100 /// * `value` - JSON value to classify.
101 ///
102 /// # Returns
103 ///
104 /// The value's coarse top-level kind.
105 #[inline(always)]
106 fn from(value: &Value) -> Self {
107 Self::of(value)
108 }
109}
110
111impl fmt::Display for JsonTopLevelKind {
112 /// Writes the stable lowercase name of this top-level kind.
113 ///
114 /// # Parameters
115 ///
116 /// * `f` - Destination formatter.
117 ///
118 /// # Returns
119 ///
120 /// `Ok(())` when the kind name is written successfully.
121 ///
122 /// # Errors
123 ///
124 /// Returns a formatting error when the destination rejects the write.
125 #[inline]
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 let name = match self {
128 Self::Object => "object",
129 Self::Array => "array",
130 Self::Other => "other",
131 };
132 f.write_str(name)
133 }
134}
135
136impl FromStr for JsonTopLevelKind {
137 type Err = &'static str;
138
139 /// Parses a top-level kind name without ASCII case sensitivity.
140 ///
141 /// # Parameters
142 ///
143 /// * `value` - Kind name to parse.
144 ///
145 /// # Returns
146 ///
147 /// The matching top-level kind.
148 ///
149 /// # Errors
150 ///
151 /// Returns a static diagnostic when `value` is not a known kind name.
152 fn from_str(value: &str) -> Result<Self, Self::Err> {
153 if value.eq_ignore_ascii_case("object") {
154 Ok(Self::Object)
155 } else if value.eq_ignore_ascii_case("array") {
156 Ok(Self::Array)
157 } else if value.eq_ignore_ascii_case("other") {
158 Ok(Self::Other)
159 } else {
160 Err("unknown JsonTopLevelKind")
161 }
162 }
163}