Skip to main content

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}