Skip to main content

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