1use std::collections::BTreeMap;
3use std::fmt;
4
5use serde::de::{self, Visitor};
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use serde_json::{Value, json};
8
9use crate::schema::RunxSchema;
10
11pub type JsonObject = BTreeMap<String, JsonValue>;
12
13impl RunxSchema for JsonValue {
14 fn json_schema() -> Value {
15 json!({})
18 }
19}
20
21impl RunxSchema for JsonNumber {
22 fn json_schema() -> Value {
23 json!({ "type": "number" })
24 }
25}
26
27#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
28#[serde(untagged)]
29pub enum JsonValue {
30 Null,
31 Bool(bool),
32 Number(JsonNumber),
33 String(String),
34 Array(Vec<JsonValue>),
35 Object(JsonObject),
36}
37
38impl JsonValue {
39 #[must_use]
40 pub fn as_object(&self) -> Option<&JsonObject> {
41 match self {
42 Self::Object(value) => Some(value),
43 _ => None,
44 }
45 }
46
47 #[must_use]
48 pub fn as_str(&self) -> Option<&str> {
49 match self {
50 Self::String(value) => Some(value),
51 _ => None,
52 }
53 }
54
55 #[must_use]
56 pub fn as_bool(&self) -> Option<bool> {
57 match self {
58 Self::Bool(value) => Some(*value),
59 _ => None,
60 }
61 }
62
63 #[must_use]
64 pub fn as_array(&self) -> Option<&Vec<JsonValue>> {
65 match self {
66 Self::Array(value) => Some(value),
67 _ => None,
68 }
69 }
70}
71
72#[must_use]
73pub fn json_object(value: Option<&JsonValue>) -> Option<&JsonObject> {
74 value.and_then(JsonValue::as_object)
75}
76
77#[must_use]
78pub fn json_object_field<'a>(object: &'a JsonObject, field: &str) -> Option<&'a JsonObject> {
79 object.get(field).and_then(JsonValue::as_object)
80}
81
82#[must_use]
83pub fn json_string_field<'a>(object: &'a JsonObject, field: &str) -> Option<&'a str> {
84 object.get(field).and_then(JsonValue::as_str)
85}
86
87#[must_use]
88pub fn json_bool_field(object: &JsonObject, field: &str) -> Option<bool> {
89 object.get(field).and_then(JsonValue::as_bool)
90}
91
92#[derive(Clone, Debug, PartialEq)]
98pub enum JsonNumber {
99 I64(i64),
100 U64(u64),
101 F64(f64),
102}
103
104impl JsonNumber {
105 #[must_use]
106 pub fn as_f64(&self) -> Option<f64> {
107 match self {
108 Self::I64(value) => Some(*value as f64),
109 Self::U64(value) => Some(*value as f64),
110 Self::F64(value) if value.is_finite() => Some(*value),
111 Self::F64(_) => None,
112 }
113 }
114}
115
116impl Serialize for JsonNumber {
117 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
118 where
119 S: Serializer,
120 {
121 match *self {
122 Self::I64(value) => serializer.serialize_i64(value),
123 Self::U64(value) => serializer.serialize_u64(value),
124 Self::F64(value) if value.is_finite() && value.fract() == 0.0 => {
125 serialize_whole_f64(value, serializer)
126 }
127 Self::F64(value) if value.is_finite() => serializer.serialize_f64(value),
128 Self::F64(_) => Err(serde::ser::Error::custom(
129 "non-finite numbers are not valid JSON",
130 )),
131 }
132 }
133}
134
135fn serialize_whole_f64<S>(value: f64, serializer: S) -> Result<S::Ok, S::Error>
136where
137 S: Serializer,
138{
139 if value >= i64::MIN as f64 && value <= i64::MAX as f64 {
140 serializer.serialize_i64(value as i64)
141 } else if value >= 0.0 && value <= u64::MAX as f64 {
142 serializer.serialize_u64(value as u64)
143 } else {
144 serializer.serialize_f64(value)
145 }
146}
147
148impl<'de> Deserialize<'de> for JsonNumber {
149 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
150 where
151 D: Deserializer<'de>,
152 {
153 deserializer.deserialize_any(JsonNumberVisitor)
154 }
155}
156
157struct JsonNumberVisitor;
158
159impl Visitor<'_> for JsonNumberVisitor {
160 type Value = JsonNumber;
161
162 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
163 formatter.write_str("a finite JSON number")
164 }
165
166 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
167 where
168 E: de::Error,
169 {
170 Ok(JsonNumber::I64(value))
171 }
172
173 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
174 where
175 E: de::Error,
176 {
177 Ok(i64::try_from(value).map_or(JsonNumber::U64(value), JsonNumber::I64))
178 }
179
180 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
181 where
182 E: de::Error,
183 {
184 if value.is_finite() {
185 Ok(JsonNumber::F64(value))
186 } else {
187 Err(E::custom("non-finite numbers are not valid JSON"))
188 }
189 }
190}
191
192impl fmt::Display for JsonNumber {
193 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
194 match *self {
195 Self::I64(value) => write!(formatter, "{value}"),
196 Self::U64(value) => write!(formatter, "{value}"),
197 Self::F64(value) if value.is_finite() && value == 0.0 => formatter.write_str("0"),
198 Self::F64(value) if value.is_finite() && value.fract() == 0.0 => {
199 write!(formatter, "{value:.0}")
200 }
201 Self::F64(value) => write!(formatter, "{value}"),
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::{JsonNumber, JsonValue};
209
210 #[test]
211 fn json_value_round_trips_objects_with_sorted_keys() -> Result<(), serde_json::Error> {
212 let value = JsonValue::Object(
213 [
214 ("z".to_owned(), JsonValue::String("last".to_owned())),
215 ("a".to_owned(), JsonValue::Number(JsonNumber::I64(1))),
216 ]
217 .into_iter()
218 .collect(),
219 );
220
221 let json = serde_json::to_string(&value)?;
222 let decoded: JsonValue = serde_json::from_str(&json)?;
223
224 assert_eq!(json, r#"{"a":1,"z":"last"}"#);
225 assert_eq!(decoded, value);
226 Ok(())
227 }
228
229 #[test]
230 fn json_value_preserves_fractional_numbers() -> Result<(), serde_json::Error> {
231 let value = JsonValue::Number(JsonNumber::F64(0.91));
232
233 let json = serde_json::to_string(&value)?;
234 let decoded: JsonValue = serde_json::from_str(&json)?;
235
236 assert_eq!(json, "0.91");
237 assert_eq!(decoded, value);
238 Ok(())
239 }
240
241 #[test]
242 fn json_number_serializes_whole_floats_as_json_integers() -> Result<(), serde_json::Error> {
243 let value = JsonValue::Number(JsonNumber::F64(1.0));
244
245 let json = serde_json::to_string(&value)?;
246
247 assert_eq!(json, "1");
248 Ok(())
249 }
250
251 #[test]
252 fn json_number_rejects_non_finite_float_serialization() {
253 let value = JsonValue::Number(JsonNumber::F64(f64::NAN));
254
255 let result = serde_json::to_string(&value);
256
257 assert!(result.is_err());
258 }
259}