Skip to main content

yog_data/schema/
plain.rs

1//! The schemas of plain types: numbers, strings, lists, maps.
2use super::{
3    BTreeMap, Decoder, HashMap, Packer, Reader, Schema, Seg, Shape, Unpacker, Value, ValueRef,
4    Writer, is_metadata_key, typed,
5};
6
7/// What a typed value's string starts with (`@item:…`).
8fn typed_mark() -> char {
9    yog_ids::sep::TYPED.chars().next().unwrap_or_default()
10}
11
12// ── Plain types ─────────────────────────────────────────────────────────────
13
14impl Schema for Value {
15    fn shape() -> Shape {
16        Shape::Any
17    }
18    fn read(v: &ValueRef<'_>, _: &mut Reader) -> Option<Self> {
19        Some(v.to_value())
20    }
21    fn write(&self) -> Value {
22        self.clone()
23    }
24}
25
26impl Schema for bool {
27    fn shape() -> Shape {
28        Shape::Bool
29    }
30    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
31        v.as_bool().or_else(|| {
32            cx.expected("true or false", v);
33            None
34        })
35    }
36    fn write(&self) -> Value {
37        Value::Bool(*self)
38    }
39    fn encode(&self, w: &mut Writer) {
40        w.bool(*self);
41    }
42    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
43        d.bool()
44    }
45    fn pack(&self, p: &mut Packer) {
46        p.put_bool(*self);
47    }
48    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
49        u.get_bool()
50    }
51}
52
53macro_rules! int_schema {
54    ($($t:ty => $to_bits:ident, $of_bits:ident);*) => {$(
55        impl Schema for $t {
56            fn shape() -> Shape {
57                Shape::Int { min: <$t>::MIN as i64, max: (<$t>::MAX as u64).min(i64::MAX as u64) as i64 }
58            }
59            fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
60                let Some(n) = v.as_i64() else {
61                    cx.expected("an integer", v);
62                    return None;
63                };
64                <$t>::try_from(n).ok().or_else(|| {
65                    cx.problem(format!("{n} is out of range ({}..={})", <$t>::MIN, <$t>::MAX));
66                    None
67                })
68            }
69            fn write(&self) -> Value {
70                Value::Int(*self as i64)
71            }
72            fn encode(&self, w: &mut Writer) {
73                w.int(*self as i64);
74            }
75            fn decode(d: &mut Decoder<'_>) -> Option<Self> {
76                d.int().and_then(|n| <$t>::try_from(n).ok())
77            }
78            fn pack(&self, p: &mut Packer) {
79                p.put_i(*self as i64);
80            }
81            fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
82                u.get_i().and_then(|n| <$t>::try_from(n).ok())
83            }
84            fn pack_list(items: &[Self], p: &mut Packer) {
85                p.put_uints(items.len(), items.iter().map(|&n| $to_bits(n as i128)));
86            }
87            fn unpack_list(u: &mut Unpacker<'_>) -> Option<Vec<Self>> {
88                u.get_uints(|v| <$t>::try_from($of_bits(v)).ok())
89            }
90            fn encode_list(items: &[Self], w: &mut Writer) {
91                w.ints(items.iter().map(|&n| n as i64));
92            }
93            fn decode_list(d: &mut Decoder<'_>) -> Option<Vec<Self>> {
94                let n = d.ints()?;
95                let mut out = Vec::with_capacity(n);
96                for _ in 0..n {
97                    out.push(<$t>::try_from(d.packed_int()?).ok()?);
98                }
99                Some(out)
100            }
101        }
102    )*};
103}
104int_schema!(
105    i8 => signed_bits, signed_of; i16 => signed_bits, signed_of; i32 => signed_bits, signed_of; i64 => signed_bits, signed_of;
106    u8 => unsigned_bits, unsigned_of; u16 => unsigned_bits, unsigned_of; u32 => unsigned_bits, unsigned_of;
107    u64 => unsigned_bits, unsigned_of; usize => unsigned_bits, unsigned_of
108);
109
110/// A signed number as a bit-packed list's bits: zigzagged, small either way.
111fn signed_bits(n: i128) -> u64 {
112    crate::binary::zigzag(n as i64)
113}
114
115fn signed_of(v: u64) -> i128 {
116    i128::from(crate::binary::unzigzag(v))
117}
118
119/// An unsigned number as a bit-packed list's bits: itself.
120fn unsigned_bits(n: i128) -> u64 {
121    n as u64
122}
123
124fn unsigned_of(v: u64) -> i128 {
125    i128::from(v)
126}
127
128macro_rules! float_schema {
129    ($($t:ty => $one:ident, $many:ident, $read_many:ident, $put:ident, $get:ident, $puts:ident, $gets:ident);*) => {$(
130        impl Schema for $t {
131            fn shape() -> Shape {
132                Shape::Float { bits: u8::BITS * std::mem::size_of::<$t>() as u32 }
133            }
134            fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
135                v.as_f64().map(|f| f as $t).or_else(|| {
136                    cx.expected("a number", v);
137                    None
138                })
139            }
140            fn write(&self) -> Value {
141                Value::Float(*self as f64)
142            }
143            fn encode(&self, w: &mut Writer) {
144                w.$one(*self);
145            }
146            fn decode(d: &mut Decoder<'_>) -> Option<Self> {
147                d.float().map(|f| f as $t)
148            }
149            fn encode_list(items: &[Self], w: &mut Writer) {
150                w.$many(items);
151            }
152            fn decode_list(d: &mut Decoder<'_>) -> Option<Vec<Self>> {
153                d.$read_many()
154            }
155            fn pack(&self, p: &mut Packer) {
156                p.$put(*self);
157            }
158            fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
159                u.$get()
160            }
161            fn pack_list(items: &[Self], p: &mut Packer) {
162                p.$puts(items);
163            }
164            fn unpack_list(u: &mut Unpacker<'_>) -> Option<Vec<Self>> {
165                u.$gets()
166            }
167        }
168    )*};
169}
170float_schema!(
171    f32 => float32, floats32, floats32, put_f32, get_f32, put_f32s, get_f32s;
172    f64 => float, floats, floats, put_f64, get_f64, put_f64s, get_f64s
173);
174
175impl Schema for String {
176    fn shape() -> Shape {
177        Shape::Str
178    }
179    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
180        match v {
181            // Plain text, the usual case: copied once, into the result.
182            ValueRef::Str(s) if !s.starts_with('@') => Some(s.to_string()),
183            ValueRef::Str(s) => match typed::classify(s) {
184                Ok(Value::Str(s)) => Some(s),
185                Ok(other) => {
186                    cx.problem(format!(
187                        "expected plain text, found {} (write `@@` for a literal `@`)",
188                        other.kind_name()
189                    ));
190                    None
191                }
192                Err(e) => {
193                    cx.problem_of(yog_ids::problem_code::BAD_TYPED, e);
194                    None
195                }
196            },
197            other => {
198                cx.expected("a string", other);
199                None
200            }
201        }
202    }
203    fn write(&self) -> Value {
204        Value::Str(self.clone())
205    }
206    fn encode(&self, w: &mut Writer) {
207        w.str(self);
208    }
209    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
210        // A typed value (`@…`) is the slow way's to tell apart.
211        d.str()
212            .filter(|s| !s.starts_with(typed_mark()))
213            .map(str::to_owned)
214    }
215    fn pack(&self, p: &mut Packer) {
216        p.put_str(self);
217    }
218    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
219        u.get_str().map(str::to_owned)
220    }
221}
222
223/// Text that may be a constant: borrowed where it's written, owned once read.
224impl Schema for std::borrow::Cow<'static, str> {
225    fn shape() -> Shape {
226        Shape::Str
227    }
228    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
229        String::read(v, cx).map(Self::Owned)
230    }
231    fn write(&self) -> Value {
232        Value::Str(self.to_string())
233    }
234    fn encode(&self, w: &mut Writer) {
235        w.str(self);
236    }
237    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
238        String::decode(d).map(Self::Owned)
239    }
240    fn pack(&self, p: &mut Packer) {
241        p.put_str(self);
242    }
243    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
244        String::unpack(u).map(Self::Owned)
245    }
246}
247
248/// A file's path, as text (a path that isn't Unicode loses what isn't).
249impl Schema for std::path::PathBuf {
250    fn shape() -> Shape {
251        Shape::Str
252    }
253    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
254        String::read(v, cx).map(Self::from)
255    }
256    fn write(&self) -> Value {
257        Value::Str(self.to_string_lossy().into_owned())
258    }
259    fn encode(&self, w: &mut Writer) {
260        w.str(&self.to_string_lossy());
261    }
262    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
263        String::decode(d).map(Self::from)
264    }
265    fn pack(&self, p: &mut Packer) {
266        p.put_str(&self.to_string_lossy());
267    }
268    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
269        u.get_str().map(Self::from)
270    }
271}
272
273/// Text everywhere but in a frame, which carries its number.
274impl Schema for crate::Atom {
275    fn shape() -> Shape {
276        Shape::Atom
277    }
278    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
279        String::read(v, cx).map(|s| crate::Atom::new(&s))
280    }
281    fn write(&self) -> Value {
282        Value::Str(self.as_str().to_owned())
283    }
284    fn encode(&self, w: &mut Writer) {
285        w.str(self.as_str());
286    }
287    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
288        d.str()
289            .filter(|s| !s.starts_with(typed_mark()))
290            .map(crate::Atom::new)
291    }
292    fn pack(&self, p: &mut Packer) {
293        p.put_u(u64::from(self.number()));
294    }
295    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
296        crate::Atom::from_number(u32::try_from(u.get_u()?).ok()?)
297    }
298}
299
300impl<T: Schema> Schema for Option<T> {
301    fn shape() -> Shape {
302        Shape::Optional(T::shape)
303    }
304    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
305        if v.is_null() {
306            Some(None)
307        } else {
308            T::read(v, cx).map(Some)
309        }
310    }
311    fn write(&self) -> Value {
312        self.as_ref().map_or(Value::Null, T::write)
313    }
314    fn encode(&self, w: &mut Writer) {
315        match self {
316            Some(v) => v.encode(w),
317            None => {
318                w.null();
319            }
320        }
321    }
322    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
323        if d.null() {
324            Some(None)
325        } else {
326            T::decode(d).map(Some)
327        }
328    }
329    fn pack(&self, p: &mut Packer) {
330        p.put_bool(self.is_some());
331        if let Some(v) = self {
332            v.pack(p);
333        }
334    }
335    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
336        if u.get_bool()? {
337            T::unpack(u).map(Some)
338        } else {
339            Some(None)
340        }
341    }
342    fn absent() -> Option<Self> {
343        Some(None)
344    }
345    fn skip(&self) -> bool {
346        self.is_none()
347    }
348}
349
350impl<T: Schema> Schema for Box<T> {
351    fn shape() -> Shape {
352        T::shape()
353    }
354    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
355        T::read(v, cx).map(Box::new)
356    }
357    fn write(&self) -> Value {
358        (**self).write()
359    }
360    fn encode(&self, w: &mut Writer) {
361        (**self).encode(w);
362    }
363    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
364        T::decode(d).map(Box::new)
365    }
366    fn pack(&self, p: &mut Packer) {
367        (**self).pack(p);
368    }
369    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
370        T::unpack(u).map(Box::new)
371    }
372    fn absent() -> Option<Self> {
373        T::absent().map(Box::new)
374    }
375    fn skip(&self) -> bool {
376        (**self).skip()
377    }
378}
379
380impl<T: Schema> Schema for Vec<T> {
381    fn shape() -> Shape {
382        Shape::List(T::shape)
383    }
384    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
385        let ValueRef::List(items) = v else {
386            cx.expected("a list", v);
387            return None;
388        };
389        let mut out = Vec::with_capacity(items.len());
390        let mut ok = true;
391        for (i, item) in items.iter().enumerate() {
392            match cx.at(Seg::Index(i), |cx| T::read(item, cx)) {
393                Some(x) => out.push(x),
394                None => ok = false,
395            }
396        }
397        ok.then_some(out)
398    }
399    fn write(&self) -> Value {
400        Value::List(self.iter().map(T::write).collect())
401    }
402    fn encode(&self, w: &mut Writer) {
403        T::encode_list(self, w);
404    }
405    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
406        T::decode_list(d)
407    }
408    fn pack(&self, p: &mut Packer) {
409        T::pack_list(self, p);
410    }
411    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
412        T::unpack_list(u)
413    }
414}
415
416/// A set: a list, in order, each once.
417impl<T: Schema + Ord + Clone> Schema for std::collections::BTreeSet<T> {
418    fn shape() -> Shape {
419        Shape::List(T::shape)
420    }
421    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
422        Vec::<T>::read(v, cx).map(Self::from_iter)
423    }
424    fn write(&self) -> Value {
425        Value::List(self.iter().map(T::write).collect())
426    }
427    fn encode(&self, w: &mut Writer) {
428        // As a list of them is written (numbers packed).
429        T::encode_list(&self.iter().cloned().collect::<Vec<_>>(), w);
430    }
431    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
432        Vec::<T>::decode(d).map(Self::from_iter)
433    }
434}
435
436/// A string that won't change: a pointer and a length, no spare capacity
437/// (`String`'s third word) — what big tables of names keep.
438impl Schema for Box<str> {
439    fn shape() -> Shape {
440        Shape::Str
441    }
442    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
443        String::read(v, cx).map(String::into_boxed_str)
444    }
445    fn write(&self) -> Value {
446        Value::Str(self.to_string())
447    }
448    fn encode(&self, w: &mut Writer) {
449        w.str(self);
450    }
451    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
452        String::decode(d).map(String::into_boxed_str)
453    }
454    fn pack(&self, p: &mut Packer) {
455        p.put_str(self);
456    }
457    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
458        u.get_str().map(Into::into)
459    }
460}
461
462/// A string shared without copying.
463impl Schema for yog_sync::Arc<str> {
464    fn shape() -> Shape {
465        Shape::Str
466    }
467    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
468        String::read(v, cx).map(Into::into)
469    }
470    fn write(&self) -> Value {
471        Value::Str(self.to_string())
472    }
473    fn encode(&self, w: &mut Writer) {
474        w.str(self);
475    }
476    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
477        String::decode(d).map(Into::into)
478    }
479    fn pack(&self, p: &mut Packer) {
480        p.put_str(self);
481    }
482    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
483        u.get_shared_str()
484    }
485}
486
487/// A list that won't change: no spare capacity.
488impl<T: Schema> Schema for Box<[T]> {
489    fn shape() -> Shape {
490        Shape::List(T::shape)
491    }
492    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
493        Vec::<T>::read(v, cx).map(Vec::into_boxed_slice)
494    }
495    fn write(&self) -> Value {
496        Value::List(self.iter().map(T::write).collect())
497    }
498    fn encode(&self, w: &mut Writer) {
499        T::encode_list(self, w);
500    }
501    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
502        T::decode_list(d).map(Vec::into_boxed_slice)
503    }
504    fn pack(&self, p: &mut Packer) {
505        T::pack_list(self, p);
506    }
507    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
508        T::unpack_list(u).map(Vec::into_boxed_slice)
509    }
510}
511
512impl<T: Schema, const N: usize> Schema for [T; N] {
513    fn shape() -> Shape {
514        Shape::List(T::shape)
515    }
516    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
517        let items = Vec::<T>::read(v, cx)?;
518        let len = items.len();
519        items.try_into().ok().or_else(|| {
520            cx.problem(format!("expected {N} items, found {len}"));
521            None
522        })
523    }
524    fn write(&self) -> Value {
525        Value::List(self.iter().map(T::write).collect())
526    }
527    fn encode(&self, w: &mut Writer) {
528        T::encode_list(self, w);
529    }
530    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
531        T::decode_list(d)?.try_into().ok()
532    }
533    fn pack(&self, p: &mut Packer) {
534        T::pack_list(self, p);
535    }
536    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
537        T::unpack_list(u)?.try_into().ok()
538    }
539}
540
541/// Nothing: what a call answers when it only says it's done.
542impl Schema for () {
543    fn shape() -> Shape {
544        Shape::Any
545    }
546    fn read(_v: &ValueRef<'_>, _cx: &mut Reader) -> Option<Self> {
547        Some(())
548    }
549    fn write(&self) -> Value {
550        Value::Null
551    }
552}
553
554/// What may fail: `{ "ok": T }` or `{ "err": E }`.
555impl<T: Schema, E: Schema> Schema for Result<T, E> {
556    fn shape() -> Shape {
557        Shape::Any
558    }
559    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
560        let ValueRef::Map(m) = v else {
561            cx.expected("{ ok } or { err }", v);
562            return None;
563        };
564        if let Some(ok) = m.get(RESULT_OK) {
565            return cx
566                .at(Seg::Key(RESULT_OK.into()), |cx| T::read(ok, cx))
567                .map(Ok);
568        }
569        let Some(err) = m.get(RESULT_ERR) else {
570            cx.expected("{ ok } or { err }", v);
571            return None;
572        };
573        cx.at(Seg::Key(RESULT_ERR.into()), |cx| E::read(err, cx))
574            .map(Err)
575    }
576    fn write(&self) -> Value {
577        let (key, value) = match self {
578            Ok(t) => (RESULT_OK, t.write()),
579            Err(e) => (RESULT_ERR, e.write()),
580        };
581        Value::Map([(key.to_owned(), value)].into_iter().collect())
582    }
583}
584
585/// The keys of a [`Result`]'s two sides.
586pub(crate) const RESULT_OK: &str = "ok";
587pub(crate) const RESULT_ERR: &str = "err";
588
589/// A pair, written `[a, b]` (a point, a size).
590impl<A: Schema, B: Schema> Schema for (A, B) {
591    fn shape() -> Shape {
592        Shape::List(Value::shape)
593    }
594    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
595        let ValueRef::List(items) = v else {
596            cx.expected("a pair [a, b]", v);
597            return None;
598        };
599        if items.len() != 2 {
600            cx.problem(format!("expected 2 items, found {}", items.len()));
601            return None;
602        }
603        let a = cx.at(Seg::Index(0), |cx| A::read(&items[0], cx));
604        let b = cx.at(Seg::Index(1), |cx| B::read(&items[1], cx));
605        Some((a?, b?))
606    }
607    fn write(&self) -> Value {
608        Value::List(vec![self.0.write(), self.1.write()])
609    }
610}
611
612fn read_map<T: Schema>(
613    v: &ValueRef,
614    cx: &mut Reader,
615    mut put: impl FnMut(String, T),
616) -> Option<()> {
617    let map = cx.map(v)?;
618    let mut ok = true;
619    for (k, item) in map.iter() {
620        if is_metadata_key(k) {
621            continue;
622        }
623        match cx.at(Seg::Key(k.to_string()), |cx| T::read(item, cx)) {
624            Some(x) => put(k.to_string(), x),
625            None => ok = false,
626        }
627    }
628    ok.then_some(())
629}
630
631impl<T: Schema> Schema for BTreeMap<String, T> {
632    fn shape() -> Shape {
633        Shape::Map(T::shape)
634    }
635    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
636        let mut out = BTreeMap::new();
637        read_map(v, cx, |k, x| {
638            out.insert(k, x);
639        })?;
640        Some(out)
641    }
642    fn write(&self) -> Value {
643        Value::Map(self.iter().map(|(k, v)| (k.clone(), v.write())).collect())
644    }
645}
646
647impl<T: Schema> Schema for HashMap<String, T> {
648    fn shape() -> Shape {
649        Shape::Map(T::shape)
650    }
651    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
652        let mut out = HashMap::new();
653        read_map(v, cx, |k, x| {
654            out.insert(k, x);
655        })?;
656        Some(out)
657    }
658    fn write(&self) -> Value {
659        let mut keys: Vec<&String> = self.keys().collect();
660        keys.sort();
661        Value::Map(
662            keys.into_iter()
663                .map(|k| (k.clone(), self[k].write()))
664                .collect(),
665        )
666    }
667}
668
669/// An object kept in file order, values of one schema.
670#[derive(Debug, Clone, PartialEq)]
671pub struct Ordered<T>(pub Vec<(String, T)>);
672
673impl<T> Default for Ordered<T> {
674    fn default() -> Self {
675        Self(Vec::new())
676    }
677}
678
679impl<T: Schema> Schema for Ordered<T> {
680    fn shape() -> Shape {
681        Shape::Map(T::shape)
682    }
683    fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
684        let mut out = Vec::new();
685        read_map(v, cx, |k, x| out.push((k, x)))?;
686        Some(Ordered(out))
687    }
688    fn write(&self) -> Value {
689        Value::Map(self.0.iter().map(|(k, v)| (k.clone(), v.write())).collect())
690    }
691}