1use std::fmt;
9
10use indexmap::IndexMap;
11use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
12use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
13use serde_json::Number;
14
15pub(crate) const MAX_SAFE_INTEGER: u64 = (1 << 53) - 1;
18
19#[derive(Debug, Clone, PartialEq)]
20pub enum OrderedValue {
21 Null,
22 Bool(bool),
23 Number(Number),
24 String(String),
25 Array(Vec<OrderedValue>),
26 Object(IndexMap<String, OrderedValue>),
28}
29
30impl OrderedValue {
31 pub fn to_json(&self) -> serde_json::Value {
33 match self {
34 Self::Null => serde_json::Value::Null,
35 Self::Bool(v) => (*v).into(),
36 Self::Number(v) => serde_json::Value::Number(v.clone()),
37 Self::String(v) => v.clone().into(),
38 Self::Array(values) => values.iter().map(Self::to_json).collect(),
39 Self::Object(map) => map
40 .iter()
41 .map(|(key, value)| (key.clone(), value.to_json()))
42 .collect::<serde_json::Map<_, _>>()
43 .into(),
44 }
45 }
46
47 pub(crate) fn from_yaml(value: &serde_yaml::Value) -> Result<Self, String> {
50 use serde_yaml::Value;
51 Ok(match value {
52 Value::Null => Self::Null,
53 Value::Bool(v) => Self::Bool(*v),
54 Value::Number(v) => {
55 let beyond = v
56 .as_i64()
57 .is_some_and(|n| n.unsigned_abs() > MAX_SAFE_INTEGER)
58 || v.as_u64().is_some_and(|n| n > MAX_SAFE_INTEGER);
59 if beyond {
60 return Err(format!(
61 "integer {v} is beyond ±(2^53 − 1), which JavaScript cannot hold exactly"
62 ));
63 }
64 Self::Number(
65 serde_json::to_value(v)
66 .map_err(|e| e.to_string())
67 .and_then(|n| match n {
68 serde_json::Value::Number(n) => Ok(n),
69 _ => Err("YAML number has no JSON form (NaN or infinity)".to_string()),
70 })?,
71 )
72 }
73 Value::String(v) => Self::String(v.clone()),
74 Value::Sequence(values) => Self::Array(
75 values
76 .iter()
77 .map(Self::from_yaml)
78 .collect::<Result<_, _>>()?,
79 ),
80 Value::Mapping(values) => {
81 let mut out = IndexMap::with_capacity(values.len());
82 for (key, value) in values {
83 let Some(key) = key.as_str() else {
84 return Err("YAML mapping contains a non-string key".into());
85 };
86 reject_unholdable_key(key)?;
87 out.insert(key.to_owned(), Self::from_yaml(value)?);
88 }
89 Self::Object(out)
90 }
91 Value::Tagged(_) => return Err("YAML tags have no JSON representation".into()),
92 })
93 }
94}
95
96pub(crate) fn reject_unholdable_key(key: &str) -> Result<(), String> {
99 if key == "__proto__" {
100 Err("key `__proto__` cannot be held as a plain property by JavaScript".into())
101 } else {
102 Ok(())
103 }
104}
105
106impl Serialize for OrderedValue {
107 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
108 match self {
109 Self::Null => serializer.serialize_unit(),
110 Self::Bool(v) => serializer.serialize_bool(*v),
111 Self::Number(v) => v.serialize(serializer),
112 Self::String(v) => serializer.serialize_str(v),
113 Self::Array(values) => {
114 let mut seq = serializer.serialize_seq(Some(values.len()))?;
115 for value in values {
116 seq.serialize_element(value)?;
117 }
118 seq.end()
119 }
120 Self::Object(map) => {
121 let mut out = serializer.serialize_map(Some(map.len()))?;
122 for (key, value) in map {
123 out.serialize_entry(key, value)?;
124 }
125 out.end()
126 }
127 }
128 }
129}
130
131impl<'de> Deserialize<'de> for OrderedValue {
134 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
135 deserializer.deserialize_any(OrderedVisitor)
136 }
137}
138
139struct OrderedVisitor;
140
141impl<'de> Visitor<'de> for OrderedVisitor {
142 type Value = OrderedValue;
143
144 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 f.write_str("a JSON value")
146 }
147
148 fn visit_bool<E>(self, v: bool) -> Result<OrderedValue, E> {
149 Ok(OrderedValue::Bool(v))
150 }
151 fn visit_i64<E>(self, v: i64) -> Result<OrderedValue, E> {
152 Ok(OrderedValue::Number(v.into()))
153 }
154 fn visit_u64<E>(self, v: u64) -> Result<OrderedValue, E> {
155 Ok(OrderedValue::Number(v.into()))
156 }
157 fn visit_i128<E: de::Error>(self, v: i128) -> Result<OrderedValue, E> {
158 i64::try_from(v)
159 .map(|v| OrderedValue::Number(v.into()))
160 .map_err(|_| E::custom("integer is beyond ±(2^53 − 1)"))
161 }
162 fn visit_u128<E: de::Error>(self, v: u128) -> Result<OrderedValue, E> {
163 u64::try_from(v)
164 .map(|v| OrderedValue::Number(v.into()))
165 .map_err(|_| E::custom("integer is beyond ±(2^53 − 1)"))
166 }
167 fn visit_f64<E: de::Error>(self, v: f64) -> Result<OrderedValue, E> {
168 Number::from_f64(v)
169 .map(OrderedValue::Number)
170 .ok_or_else(|| E::custom("NaN and Infinity cannot be written as YAML"))
171 }
172 fn visit_str<E>(self, v: &str) -> Result<OrderedValue, E> {
173 Ok(OrderedValue::String(v.to_owned()))
174 }
175 fn visit_unit<E>(self) -> Result<OrderedValue, E> {
176 Ok(OrderedValue::Null)
177 }
178 fn visit_none<E>(self) -> Result<OrderedValue, E> {
179 Ok(OrderedValue::Null)
180 }
181 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<OrderedValue, A::Error> {
182 let mut out = Vec::new();
183 while let Some(value) = seq.next_element()? {
184 out.push(value);
185 }
186 Ok(OrderedValue::Array(out))
187 }
188 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<OrderedValue, A::Error> {
189 let mut out = IndexMap::new();
190 while let Some((key, value)) = map.next_entry::<String, OrderedValue>()? {
191 out.insert(key, value);
192 }
193 Ok(OrderedValue::Object(out))
194 }
195}
196
197pub(crate) fn plain(value: &serde_yaml::Value) -> String {
200 match OrderedValue::from_yaml(value) {
201 Ok(json) => serde_json::to_string(&json).unwrap_or_default(),
202 Err(_) => serde_yaml::to_string(value)
203 .map(|text| text.trim_end().to_owned())
204 .unwrap_or_default(),
205 }
206}