Skip to main content

substrate_data/schema/
with.rs

1//! Field codecs: `#[data(with = "…")]` reads and writes a field through a
2//! type other than its own — like serde's `with`. The field keeps the plain
3//! type a program uses (`String`), the format says what it holds (a
4//! reference to a file), checked and resolved by the [`Reader`]'s
5//! [`Resolve`](crate::schema::Resolve).
6//!
7//! ```
8//! use substrate_data::with::{Opt, RefTo};
9//!
10//! substrate_data::kind!(File = "file", User = "user");
11//!
12//! #[derive(substrate_data::Schema)]
13//! struct Attachment {
14//!     /// `@file:…`, kept as the plain id.
15//!     #[data(with = "RefTo<File>")]
16//!     file: String,
17//!     #[data(with = "Opt<RefTo<User>>")]
18//!     owner: Option<String>,
19//! }
20//!
21//! let a: Attachment =
22//!     substrate_data::from_text(r#"{ file: "@file:images/logo.png" }"#).unwrap().value;
23//! assert_eq!(a.file, "images/logo.png");
24//! assert_eq!(a.owner, None);
25//! ```
26
27use std::collections::{BTreeMap, HashMap};
28use std::marker::PhantomData;
29
30pub use super::expand::Expand;
31use crate::binary::{Decoder, Writer};
32use crate::frame::{Packer, Unpacker};
33use crate::model::borrowed::ValueRef;
34use crate::model::value::{Map, Ref, Seg, Value};
35use crate::schema::{Kinds, Reader, Schema, Shape, is_metadata_key};
36
37/// How a field of type `F` is read and written.
38pub trait With<F> {
39    /// The field's shape.
40    fn shape() -> Shape;
41    /// Reads the field.
42    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<F>;
43    /// Writes the field.
44    fn write(f: &F) -> Value;
45    /// The field's value when it isn't there (`None`: it's required).
46    #[must_use]
47    fn absent() -> Option<F> {
48        None
49    }
50    /// Whether a struct leaves the field out.
51    fn skip(_f: &F) -> bool {
52        false
53    }
54    /// The field in the binary form, straight: its value encoded unless
55    /// the codec knows better.
56    fn encode(f: &F, w: &mut Writer) {
57        w.value(&Self::write(f));
58    }
59    /// The field as text, straight: its value as text unless the codec
60    /// knows better (see [`Schema::write_text`]).
61    fn write_text(f: &F, w: &mut crate::text::Writer) {
62        w.value(&Self::write(f));
63    }
64    /// The field from the binary form, straight; `None` sends the read the
65    /// slow way (see [`Schema::decode`]).
66    fn decode(d: &mut Decoder<'_>) -> Option<F> {
67        let arena = bumpalo::Bump::new();
68        let v = d.value_ref(&arena).ok()?;
69        let mut cx = Reader::new();
70        let f = Self::read(&v, &mut cx)?;
71        cx.problems.is_empty().then_some(f)
72    }
73    /// The field packed into a frame: its binary form as bytes unless the
74    /// codec knows better.
75    fn pack(f: &F, p: &mut Packer) {
76        let mut w = Writer::body();
77        Self::encode(f, &mut w);
78        p.put_bytes(&w.finish());
79    }
80    /// The field out of a frame.
81    fn unpack(u: &mut Unpacker<'_>) -> Option<F> {
82        let mut d = Decoder::body(u.bytes()?);
83        let f = Self::decode(&mut d)?;
84        d.at_end().then_some(f)
85    }
86}
87
88/// A reference of kind(s) `K`, kept as the resolved id.
89#[derive(Debug)]
90pub struct RefTo<K>(PhantomData<K>);
91
92impl<K: Kinds> With<K::Id> for RefTo<K> {
93    fn shape() -> Shape {
94        super::ref_shape::<K>()
95    }
96    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<K::Id> {
97        super::read_id::<K>(v, cx)
98    }
99    fn write(f: &K::Id) -> Value {
100        Value::Ref(Ref::new(
101            K::first(),
102            crate::model::value::RefKey::spelled(&crate::schema::RefId::spell(f)),
103        ))
104    }
105    fn write_text(f: &K::Id, w: &mut crate::text::Writer) {
106        w.reference(K::first().as_str(), &crate::schema::RefId::spell(f));
107    }
108    fn pack(f: &K::Id, p: &mut Packer) {
109        f.pack(p);
110    }
111    fn unpack(u: &mut Unpacker<'_>) -> Option<K::Id> {
112        K::Id::unpack(u)
113    }
114}
115
116/// Text as it's spelled — markup's attribute, which may spell a reference
117/// (`@file:images/logo.png`): a string kept as written, a typed value as its
118/// spelling. Nothing is checked or resolved here.
119#[derive(Debug)]
120pub struct Spelled;
121
122impl With<String> for Spelled {
123    fn shape() -> Shape {
124        Shape::Str
125    }
126    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<String> {
127        if let ValueRef::Str(s) = v {
128            return Some(s.to_string());
129        }
130        let spelled = crate::typed::spelling(&v.to_value());
131        if spelled.is_none() {
132            cx.expected(super::Expected::Text, v);
133        }
134        spelled
135    }
136    fn write(f: &String) -> Value {
137        Value::Str(f.clone())
138    }
139    fn write_text(f: &String, w: &mut crate::text::Writer) {
140        w.str(f);
141    }
142}
143
144/// An optional field through `W`.
145#[derive(Debug)]
146pub struct Opt<W>(PhantomData<W>);
147
148impl<F, W: With<F>> With<Option<F>> for Opt<W> {
149    fn shape() -> Shape {
150        Shape::Optional(W::shape)
151    }
152    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Option<F>> {
153        if v.is_null() {
154            Some(None)
155        } else {
156            W::read(v, cx).map(Some)
157        }
158    }
159    fn write(f: &Option<F>) -> Value {
160        f.as_ref().map_or(Value::Null, W::write)
161    }
162    fn write_text(f: &Option<F>, w: &mut crate::text::Writer) {
163        match f {
164            Some(f) => W::write_text(f, w),
165            None => {
166                w.null();
167            }
168        }
169    }
170    fn absent() -> Option<Option<F>> {
171        Some(None)
172    }
173    fn skip(f: &Option<F>) -> bool {
174        f.is_none()
175    }
176}
177
178/// A list, each item through `W`.
179#[derive(Debug)]
180pub struct List<W>(PhantomData<W>);
181
182impl<F, W: With<F>> With<Vec<F>> for List<W> {
183    fn shape() -> Shape {
184        Shape::List(W::shape)
185    }
186    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Vec<F>> {
187        let ValueRef::List(items) = v else {
188            cx.expected(super::Expected::List, v);
189            return None;
190        };
191        let mut out = Vec::with_capacity(items.len());
192        let mut ok = true;
193        for (i, item) in items.iter().enumerate() {
194            match cx.at(Seg::Index(i), |cx| W::read(item, cx)) {
195                Some(x) => out.push(x),
196                None => ok = false,
197            }
198        }
199        ok.then_some(out)
200    }
201    fn write(f: &Vec<F>) -> Value {
202        Value::List(f.iter().map(W::write).collect())
203    }
204    fn write_text(f: &Vec<F>, w: &mut crate::text::Writer) {
205        w.list(f.len());
206        for item in f {
207            W::write_text(item, w);
208        }
209    }
210}
211
212/// An object of any keys, each value through `W`.
213#[derive(Debug)]
214pub struct Values<W>(PhantomData<W>);
215
216impl<F, W: With<F>> With<BTreeMap<String, F>> for Values<W> {
217    fn shape() -> Shape {
218        Shape::Map(W::shape)
219    }
220    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<BTreeMap<String, F>> {
221        read_values::<F, W, _>(v, cx)
222    }
223    fn write(f: &BTreeMap<String, F>) -> Value {
224        write_values::<F, W>(f.iter())
225    }
226    fn write_text(f: &BTreeMap<String, F>, w: &mut crate::text::Writer) {
227        w.map(f.len());
228        for (k, v) in f {
229            w.key(k);
230            W::write_text(v, w);
231        }
232    }
233}
234
235impl<F, W: With<F>> With<HashMap<String, F>> for Values<W> {
236    fn shape() -> Shape {
237        Shape::Map(W::shape)
238    }
239    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<HashMap<String, F>> {
240        read_values::<F, W, _>(v, cx)
241    }
242    fn write(f: &HashMap<String, F>) -> Value {
243        write_values::<F, W>(f.iter())
244    }
245}
246
247/// An object's entries (its notes left out), each value through `W`.
248fn read_values<F, W: With<F>, M: Default + Extend<(String, F)>>(
249    v: &ValueRef<'_>,
250    cx: &mut Reader<'_>,
251) -> Option<M> {
252    let map = cx.map(v)?;
253    let mut out = M::default();
254    let mut ok = true;
255    for (k, item) in map.iter().filter(|(k, _)| !is_metadata_key(k)) {
256        match cx.at(Seg::Key(k.to_string()), |cx| W::read(item, cx)) {
257            Some(x) => out.extend([(k.to_string(), x)]),
258            None => ok = false,
259        }
260    }
261    ok.then_some(out)
262}
263
264fn write_values<'a, F: 'a, W: With<F>>(
265    entries: impl Iterator<Item = (&'a String, &'a F)>,
266) -> Value {
267    Value::Map(
268        entries
269            .map(|(k, v)| (k.clone(), W::write(v)))
270            .collect::<Map>(),
271    )
272}
273
274/// The field's own schema — `with` spelled out (`List<Plain>` for a `Vec<T: Schema>`).
275#[derive(Debug)]
276pub struct Plain;
277
278impl<F: Schema> With<F> for Plain {
279    fn shape() -> Shape {
280        F::shape()
281    }
282    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<F> {
283        F::read(v, cx)
284    }
285    fn write(f: &F) -> Value {
286        f.write()
287    }
288    fn absent() -> Option<F> {
289        F::absent()
290    }
291    fn skip(f: &F) -> bool {
292        f.skip()
293    }
294    fn write_text(f: &F, w: &mut crate::text::Writer) {
295        f.write_text(w);
296    }
297    fn encode(f: &F, w: &mut Writer) {
298        f.encode(w);
299    }
300    fn decode(d: &mut Decoder<'_>) -> Option<F> {
301        F::decode(d)
302    }
303    fn pack(f: &F, p: &mut Packer) {
304        f.pack(p);
305    }
306    fn unpack(u: &mut Unpacker<'_>) -> Option<F> {
307        F::unpack(u)
308    }
309}
310
311/// Bytes as they are (`Vec<u8>`): a payload a format doesn't look into.
312#[derive(Debug)]
313pub struct Raw;
314
315impl With<Vec<u8>> for Raw {
316    fn shape() -> Shape {
317        Shape::Bytes
318    }
319    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Vec<u8>> {
320        match v {
321            ValueRef::Bytes(b) => Some(b.to_vec()),
322            // As text: `@bytes:<base64>`.
323            ValueRef::Str(s) => {
324                if let crate::typed::Spelled::Bytes(b) = crate::typed::classify(s)
325                    .map_err(|e| cx.problem_of(crate::Problem::BadTyped, e.to_string()))
326                    .ok()?
327                {
328                    Some(b)
329                } else {
330                    cx.expected(super::Expected::Bytes, v);
331                    None
332                }
333            }
334            other => {
335                cx.expected(super::Expected::Bytes, other);
336                None
337            }
338        }
339    }
340    fn write(f: &Vec<u8>) -> Value {
341        Value::Bytes(f.clone())
342    }
343    fn write_text(f: &Vec<u8>, w: &mut crate::text::Writer) {
344        w.bytes(f);
345    }
346    fn encode(f: &Vec<u8>, w: &mut Writer) {
347        w.bytes(f);
348    }
349    fn decode(d: &mut Decoder<'_>) -> Option<Vec<u8>> {
350        d.bytes().map(<[u8]>::to_vec)
351    }
352    fn pack(f: &Vec<u8>, p: &mut Packer) {
353        p.put_bytes(f);
354    }
355    fn unpack(u: &mut Unpacker<'_>) -> Option<Vec<u8>> {
356        u.bytes().map(<[u8]>::to_vec)
357    }
358    fn absent() -> Option<Vec<u8>> {
359        Some(Vec::new())
360    }
361}