1use std::{borrow::Cow, collections::HashMap};
29
30#[cfg(feature = "serde")]
31use serde::{
32 Deserialize, Deserializer, Serialize, Serializer,
33 de::{self, MapAccess, SeqAccess, Visitor},
34 ser::SerializeStruct,
35};
36
37use crate::{Number, Version, save::Error};
38
39#[derive(Debug)]
45pub enum Value<'a> {
46 Null,
47 Bool(bool),
48 Number(Number),
49 String(Cow<'a, str>),
50 Array(Vec<Value<'a>>),
51 Object(Versioned<'a>),
52 Handle(Handle),
53}
54
55#[cfg(feature = "serde")]
56impl Serialize for Value<'_> {
57 fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
58 match self {
59 Self::Null => ser.serialize_none(),
60 Self::Bool(b) => ser.serialize_bool(*b),
61 Self::Number(n) => n.serialize(ser),
62 Self::String(s) => ser.serialize_str(s),
63 Self::Array(a) => a.serialize(ser),
64 Self::Object(v) => v.serialize(ser),
65 Self::Handle(h) => h.serialize(ser),
66 }
67 }
68}
69
70#[cfg(feature = "serde")]
71impl<'de> Deserialize<'de> for Value<'static> {
72 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73 where
74 D: Deserializer<'de>,
75 {
76 struct Inner;
77
78 impl<'de> Visitor<'de> for Inner {
79 type Value = Value<'static>;
80
81 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
82 f.write_str("a valid Value")
83 }
84
85 fn visit_unit<E: de::Error>(self) -> Result<Value<'static>, E> {
86 Ok(Value::Null)
87 }
88
89 fn visit_none<E: de::Error>(self) -> Result<Value<'static>, E> {
90 Ok(Value::Null)
91 }
92
93 fn visit_some<D>(self, deserializer: D) -> Result<Value<'static>, D::Error>
94 where
95 D: Deserializer<'de>,
96 {
97 Value::deserialize(deserializer)
98 }
99
100 fn visit_bool<E: de::Error>(self, v: bool) -> Result<Value<'static>, E> {
101 Ok(Value::Bool(v))
102 }
103
104 fn visit_u64<E: de::Error>(self, v: u64) -> Result<Value<'static>, E> {
105 Ok(Value::Number(Number::U64(v)))
106 }
107
108 fn visit_i64<E: de::Error>(self, v: i64) -> Result<Value<'static>, E> {
109 Ok(Value::Number(Number::I64(v)))
110 }
111
112 fn visit_f64<E: de::Error>(self, v: f64) -> Result<Value<'static>, E> {
113 Ok(Value::Number(Number::F64(v)))
114 }
115
116 fn visit_str<E: de::Error>(self, v: &str) -> Result<Value<'static>, E> {
117 if let Some(n) = Number::from_sentinel(v) {
118 return Ok(Value::Number(n));
119 }
120 Ok(Value::String(Cow::Owned(v.to_owned())))
121 }
122
123 fn visit_string<E: de::Error>(self, v: String) -> Result<Value<'static>, E> {
124 if let Some(n) = Number::from_sentinel(&v) {
125 return Ok(Value::Number(n));
126 }
127 Ok(Value::String(Cow::Owned(v)))
128 }
129
130 fn visit_seq<A>(self, mut seq: A) -> Result<Value<'static>, A::Error>
131 where
132 A: SeqAccess<'de>,
133 {
134 let mut values = Vec::with_capacity(seq.size_hint().unwrap_or(0));
135 while let Some(v) = seq.next_element()? {
136 values.push(v);
137 }
138 Ok(Value::Array(values))
139 }
140
141 fn visit_map<A>(self, mut map: A) -> Result<Value<'static>, A::Error>
142 where
143 A: MapAccess<'de>,
144 {
145 let mut version: Option<Version> = None;
147 let mut handle_name: Option<String> = None;
148 let mut fields: HashMap<Cow<'static, str>, Value<'static>> = HashMap::new();
149
150 while let Some(key) = map.next_key::<String>()? {
151 match key.as_str() {
152 "$version" => {
153 version = Some(map.next_value()?);
154 }
155 "$handle" => {
156 handle_name = Some(map.next_value()?);
157 }
158 _ => {
159 let value = map.next_value()?;
160 fields.insert(Cow::Owned(key), value);
161 }
162 }
163 }
164
165 if let Some(name) = handle_name {
166 if version.is_some() || !fields.is_empty() {
167 return Err(de::Error::custom(
168 "handle object must contain only a \"$handle\" field",
169 ));
170 }
171 return Ok(Value::Handle(Handle(name)));
172 }
173
174 if let Some(version) = version {
175 let record = Record { record: fields };
176 return Ok(Value::Object(Versioned { record, version }));
177 }
178
179 Err(de::Error::custom(
180 "map must contain either \"$version\" or \"$handle\"",
181 ))
182 }
183 }
184
185 deserializer.deserialize_any(Inner)
186 }
187}
188
189impl From<Handle> for Value<'_> {
190 fn from(handle: Handle) -> Self {
191 Self::Handle(handle)
192 }
193}
194
195impl Value<'_> {
196 pub fn into_owned(self) -> Value<'static> {
204 match self {
205 Self::Null => Value::Null,
206 Self::Bool(b) => Value::Bool(b),
207 Self::Number(n) => Value::Number(n),
208 Self::String(s) => Value::String(Cow::Owned(s.into_owned())),
209 Self::Array(values) => {
210 Value::Array(values.into_iter().map(Value::into_owned).collect())
211 }
212 Self::Object(versioned) => Value::Object(versioned.into_owned()),
213 Self::Handle(handle) => Value::Handle(handle),
214 }
215 }
216}
217
218#[derive(Debug)]
226#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
227#[cfg_attr(feature = "serde", serde(transparent))]
228pub struct Record<'a> {
229 record: HashMap<Cow<'a, str>, Value<'a>>,
230}
231
232impl<'a> Record<'a> {
233 pub fn empty() -> Self {
235 Self {
236 record: HashMap::new(),
237 }
238 }
239
240 pub fn contains_key(&self, key: &str) -> bool {
242 self.record.contains_key(key)
243 }
244
245 pub fn get(&self, key: &str) -> Option<&Value<'a>> {
247 self.record.get(key)
248 }
249
250 pub fn len(&self) -> usize {
253 self.record.len()
254 }
255
256 pub fn is_empty(&self) -> bool {
258 self.record.is_empty()
259 }
260
261 pub fn keys(&self) -> Keys<'_, 'a> {
263 Keys {
264 inner: self.record.keys(),
265 }
266 }
267
268 pub fn insert<K, V>(&mut self, key: K, value: V) -> crate::save::Result<Option<Value<'a>>>
275 where
276 K: Into<Cow<'a, str>>,
277 V: Into<Value<'a>>,
278 {
279 let key = key.into();
280 if crate::is_reserved(&key) {
281 return Err(Error::message(format!(
282 "record key {:?} is reserved (keys starting with `$` are reserved for the \
283 save/load framework)",
284 key,
285 )));
286 }
287
288 Ok(self.record.insert(key, value.into()))
289 }
290
291 pub fn into_value(self, version: Version) -> Value<'a> {
295 Value::Object(Versioned::new(self, version))
296 }
297
298 pub fn into_owned(self) -> Record<'static> {
301 Record {
302 record: self
303 .record
304 .into_iter()
305 .map(|(key, value)| (Cow::Owned(key.into_owned()), value.into_owned()))
306 .collect(),
307 }
308 }
309}
310
311pub struct Keys<'r, 'a> {
313 inner: std::collections::hash_map::Keys<'r, Cow<'a, str>, Value<'a>>,
314}
315
316impl<'r, 'a> Iterator for Keys<'r, 'a> {
317 type Item = &'r str;
318
319 fn next(&mut self) -> Option<Self::Item> {
320 self.inner.next().map(|k| k.as_ref())
321 }
322
323 fn size_hint(&self) -> (usize, Option<usize>) {
324 self.inner.size_hint()
325 }
326}
327
328impl ExactSizeIterator for Keys<'_, '_> {}
329
330impl<'a> FromIterator<(Cow<'a, str>, Value<'a>)> for Record<'a> {
331 fn from_iter<I: IntoIterator<Item = (Cow<'a, str>, Value<'a>)>>(itr: I) -> Self {
332 Self {
333 record: itr.into_iter().collect(),
334 }
335 }
336}
337
338#[derive(Debug)]
343#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
344pub struct Versioned<'a> {
345 #[cfg_attr(feature = "serde", serde(flatten))]
346 record: Record<'a>,
347 #[cfg_attr(feature = "serde", serde(rename = "$version"))]
348 version: Version,
349}
350
351impl<'a> Versioned<'a> {
352 pub(crate) fn new(record: Record<'a>, version: Version) -> Self {
353 Self { record, version }
354 }
355
356 pub(crate) fn version(&self) -> Version {
357 self.version
358 }
359
360 pub(crate) fn record(&self) -> &Record<'a> {
361 &self.record
362 }
363
364 pub(crate) fn into_owned(self) -> Versioned<'static> {
365 Versioned {
366 record: self.record.into_owned(),
367 version: self.version,
368 }
369 }
370}
371
372#[derive(Debug, Clone)]
379pub struct Handle(String);
380
381impl Handle {
382 pub(crate) fn new(string: String) -> Self {
383 Self(string)
384 }
385
386 pub(crate) fn as_str(&self) -> &str {
387 &self.0
388 }
389}
390
391#[cfg(feature = "serde")]
392impl Serialize for Handle {
393 fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
394 let mut handle = ser.serialize_struct("Handle", 1)?;
395 handle.serialize_field("$handle", &self.0)?;
396 handle.end()
397 }
398}
399
400#[cfg(feature = "serde")]
401impl<'de> Deserialize<'de> for Handle {
402 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
403 where
404 D: Deserializer<'de>,
405 {
406 #[derive(Deserialize)]
407 struct Helper {
408 #[serde(rename = "$handle")]
409 handle: String,
410 }
411 let helper = Helper::deserialize(deserializer)?;
412 Ok(Handle(helper.handle))
413 }
414}
415
416#[cfg(test)]
417mod tests {
418 use super::*;
419
420 #[test]
421 fn insert_rejects_reserved_key() {
422 let mut record = Record::empty();
423 record
424 .insert("$version", Value::Null)
425 .expect_err("reserved key must be rejected");
426 record
427 .insert("ok", Value::Bool(true))
428 .expect("normal key must be accepted");
429 assert!(record.contains_key("ok"));
430 assert_eq!(record.len(), 1);
431 }
432
433 #[cfg(feature = "disk")]
434 #[test]
435 fn deserialize_rejects_handle_with_extra_fields() {
436 let json = r#"{ "$handle": "a.bin", "$version": "0.0" }"#;
437 serde_json::from_str::<Value<'static>>(json)
438 .expect_err("handle object with extra fields must be rejected");
439 }
440
441 #[cfg(feature = "disk")]
442 #[test]
443 fn deserialize_rejects_object_without_version_or_handle() {
444 let json = r#"{ "field": 1 }"#;
445 serde_json::from_str::<Value<'static>>(json)
446 .expect_err("object without $version or $handle must be rejected");
447 }
448
449 #[cfg(feature = "disk")]
455 #[test]
456 fn float_sentinel_strings_deserialize_as_numbers() {
457 for (s, is_nan) in [("nan", true), ("inf", false), ("neg_inf", false)] {
458 let json = serde_json::to_string(&Value::String(Cow::Borrowed(s))).unwrap();
459 let back: Value<'static> = serde_json::from_str(&json).unwrap();
460 match back {
461 Value::Number(Number::F64(v)) if is_nan => assert!(v.is_nan()),
462 Value::Number(Number::F64(_)) => {}
463 other => panic!("sentinel {s:?} deserialized as {other:?}, expected Number::F64"),
464 }
465 }
466 }
467}