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