Skip to main content

toon/
lib.rs

1#![forbid(unsafe_code)]
2
3pub mod cli;
4pub mod decode;
5pub mod encode;
6pub mod error;
7pub mod options;
8pub mod shared;
9
10#[cfg(feature = "wasm")]
11pub mod wasm;
12
13pub use decode::{
14    decode, decode_from_lines, decode_stream, decode_stream_sync, try_decode,
15    try_decode_from_lines, try_decode_stream, try_decode_stream_sync,
16};
17pub use encode::{encode, encode_lines, encode_stream_events};
18pub use options::{
19    DecodeOptions, DecodeStreamOptions, EncodeOptions, EncodeReplacer, ResolvedDecodeOptions,
20    ResolvedEncodeOptions,
21};
22
23/// Convenience wrapper: parse JSON text and encode to TOON.
24///
25/// For lower-level control, parse JSON yourself and call [`encode()`].
26///
27/// # Errors
28/// Returns an error if the JSON input is invalid.
29pub fn json_to_toon(json: &str) -> crate::error::Result<String> {
30    let value: serde_json::Value =
31        serde_json::from_str(json).map_err(|e| crate::error::ToonError::json_parse(&e))?;
32    Ok(encode(value, None))
33}
34
35/// Convenience wrapper: decode TOON and return compact JSON text.
36///
37/// For lower-level control, call [`try_decode`] and handle [`JsonValue`] directly.
38///
39/// # Errors
40/// Returns an error if the TOON input is invalid.
41pub fn toon_to_json(toon: &str) -> crate::error::Result<String> {
42    let value = try_decode(toon, None)?;
43    let value = serde_json::Value::from(value);
44    serde_json::to_string(&value).map_err(|e| crate::error::ToonError::json_stringify(&e))
45}
46
47pub type JsonPrimitive = StringOrNumberOrBoolOrNull;
48pub type JsonObject = Vec<(String, JsonValue)>;
49pub type JsonArray = Vec<JsonValue>;
50
51#[derive(Debug, Clone, PartialEq)]
52pub enum JsonValue {
53    Primitive(JsonPrimitive),
54    Array(JsonArray),
55    Object(JsonObject),
56}
57
58#[derive(Debug, Clone, PartialEq)]
59pub enum JsonStreamEvent {
60    StartObject,
61    EndObject,
62    StartArray { length: usize },
63    EndArray,
64    Key { key: String, was_quoted: bool },
65    Primitive { value: JsonPrimitive },
66}
67
68#[derive(Debug, Clone, PartialEq)]
69pub enum StringOrNumberOrBoolOrNull {
70    String(String),
71    Number(f64),
72    Bool(bool),
73    Null,
74}
75
76impl StringOrNumberOrBoolOrNull {
77    #[must_use]
78    pub fn from_f64(value: f64) -> Self {
79        if !value.is_finite() {
80            return Self::Null;
81        }
82        if value == 0.0 {
83            return Self::Number(0.0);
84        }
85        Self::Number(value)
86    }
87}
88
89impl From<StringOrNumberOrBoolOrNull> for JsonValue {
90    fn from(value: StringOrNumberOrBoolOrNull) -> Self {
91        Self::Primitive(value)
92    }
93}
94
95impl From<String> for JsonValue {
96    fn from(value: String) -> Self {
97        Self::Primitive(StringOrNumberOrBoolOrNull::String(value))
98    }
99}
100
101impl From<&str> for JsonValue {
102    fn from(value: &str) -> Self {
103        Self::Primitive(StringOrNumberOrBoolOrNull::String(value.to_string()))
104    }
105}
106
107impl From<bool> for JsonValue {
108    fn from(value: bool) -> Self {
109        Self::Primitive(StringOrNumberOrBoolOrNull::Bool(value))
110    }
111}
112
113impl From<f64> for JsonValue {
114    fn from(value: f64) -> Self {
115        Self::Primitive(StringOrNumberOrBoolOrNull::from_f64(value))
116    }
117}
118
119#[allow(clippy::cast_precision_loss)]
120impl From<i64> for JsonValue {
121    fn from(value: i64) -> Self {
122        Self::Primitive(StringOrNumberOrBoolOrNull::Number(value as f64))
123    }
124}
125
126#[allow(clippy::use_self)]
127impl From<Vec<JsonValue>> for JsonValue {
128    fn from(value: Vec<JsonValue>) -> Self {
129        Self::Array(value)
130    }
131}
132
133impl From<JsonObject> for JsonValue {
134    fn from(value: JsonObject) -> Self {
135        Self::Object(value)
136    }
137}
138
139impl From<serde_json::Value> for JsonValue {
140    fn from(value: serde_json::Value) -> Self {
141        match value {
142            serde_json::Value::Null => Self::Primitive(StringOrNumberOrBoolOrNull::Null),
143            serde_json::Value::Bool(value) => {
144                Self::Primitive(StringOrNumberOrBoolOrNull::Bool(value))
145            }
146            serde_json::Value::Number(value) => {
147                let number = value
148                    .as_f64()
149                    .unwrap_or_else(|| value.to_string().parse::<f64>().unwrap_or(f64::NAN));
150                Self::Primitive(StringOrNumberOrBoolOrNull::from_f64(number))
151            }
152            serde_json::Value::String(value) => {
153                Self::Primitive(StringOrNumberOrBoolOrNull::String(value))
154            }
155            serde_json::Value::Array(values) => {
156                Self::Array(values.into_iter().map(Self::from).collect())
157            }
158            serde_json::Value::Object(map) => {
159                let mut entries = Vec::with_capacity(map.len());
160                for (key, value) in map {
161                    entries.push((key, Self::from(value)));
162                }
163                Self::Object(entries)
164            }
165        }
166    }
167}
168
169impl From<JsonValue> for serde_json::Value {
170    fn from(value: JsonValue) -> Self {
171        match value {
172            JsonValue::Primitive(p) => match p {
173                StringOrNumberOrBoolOrNull::String(value) => Self::String(value),
174                StringOrNumberOrBoolOrNull::Number(value) => {
175                    serde_json::Number::from_f64(value).map_or(Self::Null, Self::Number)
176                }
177                StringOrNumberOrBoolOrNull::Bool(value) => Self::Bool(value),
178                StringOrNumberOrBoolOrNull::Null => Self::Null,
179            },
180            JsonValue::Array(arr) => Self::Array(arr.into_iter().map(Self::from).collect()),
181            JsonValue::Object(obj) => {
182                let mut map = serde_json::Map::new();
183                for (key, val) in obj {
184                    map.insert(key, Self::from(val));
185                }
186                Self::Object(map)
187            }
188        }
189    }
190}