Skip to main content

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