1mod fields;
8mod one_or_list;
9mod plain;
10mod reader;
11mod shape;
12mod typed_values;
13pub mod with;
14
15pub use fields::{Fields, field_name};
16pub use one_or_list::OneOrList;
17pub use plain::*;
18pub use reader::*;
19pub use shape::*;
20pub use typed_values::*;
21
22use std::collections::{BTreeMap, HashMap};
23use std::fmt;
24use std::marker::PhantomData;
25
26use crate::binary::{Decoder, Writer};
27use crate::diagnostic::{Diagnostic, Errors};
28use crate::frame::{Packer, Unpacker};
29use crate::model::borrowed::{MapRef, ValueRef};
30use crate::model::value::{Map, Path, Ref, Res, Seg, Value};
31use crate::text::{self, Doc, LineCol};
32use crate::typed;
33
34pub trait Schema: Sized {
36 fn shape() -> Shape;
38
39 fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self>;
42
43 fn write(&self) -> Value;
45
46 fn absent() -> Option<Self> {
48 None
49 }
50
51 fn skip(&self) -> bool {
53 false
54 }
55
56 fn encode(&self, w: &mut Writer) {
59 w.value(&self.write());
60 }
61
62 fn decode(d: &mut Decoder<'_>) -> Option<Self> {
67 let arena = bumpalo::Bump::new();
68 let v = d.value_ref(&arena).ok()?;
69 read_ref_value(&v).ok()
70 }
71
72 fn pack(&self, p: &mut Packer) {
75 p.put_tagged(self);
76 }
77
78 fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
81 u.get_tagged()
82 }
83
84 fn pack_list(items: &[Self], p: &mut Packer) {
86 let at = p.begin_list(items.len());
87 for item in items {
88 item.pack(p);
89 }
90 p.end(at);
91 }
92
93 fn unpack_list(u: &mut Unpacker<'_>) -> Option<Vec<Self>> {
95 let (n, end) = u.list()?;
96 let mut out = Vec::with_capacity(n);
97 for _ in 0..n {
98 out.push(Self::unpack(u)?);
99 }
100 u.skip_to(end)?;
101 Some(out)
102 }
103
104 fn encode_list(items: &[Self], w: &mut Writer) {
106 w.list(items.len());
107 for item in items {
108 item.encode(w);
109 }
110 }
111
112 fn decode_list(d: &mut Decoder<'_>) -> Option<Vec<Self>> {
114 let n = d.list()?;
115 let mut out = Vec::with_capacity(n);
116 for _ in 0..n {
117 out.push(Self::decode(d)?);
118 }
119 d.leave();
120 Some(out)
121 }
122}
123
124pub const WRAPPED: &str = yog_ids::word::VALUE;
128
129#[doc(hidden)]
132pub fn flattens<T: Schema>() -> bool {
133 matches!(T::shape(), Shape::Struct(_))
134}
135
136pub fn to_bytes<T: Schema>(v: &T) -> Vec<u8> {
138 let mut w = Writer::new();
139 v.encode(&mut w);
140 w.finish()
141}
142
143pub fn field_names(shape: &Shape) -> Vec<&'static str> {
145 match shape {
146 Shape::Struct(s) => s.fields.iter().map(|f| f.name).collect(),
147 _ => Vec::new(),
148 }
149}
150
151pub fn read_both<A: Schema, B: Schema>(
154 v: &ValueRef,
155 cx: &mut Reader,
156 also: &[&'static str],
157) -> Option<(A, B)> {
158 let (an, bn) = (field_names(&A::shape()), field_names(&B::shape()));
159 let mut b_keys = bn.clone();
160 b_keys.extend_from_slice(also);
161 let mut a_keys = an;
162 a_keys.extend_from_slice(also);
163 let a = cx.with_allowed_all(&b_keys, |cx| A::read(v, cx));
164 let b = cx.with_allowed_all(&a_keys, |cx| B::read(v, cx));
165 Some((a?, b?))
166}
167
168pub fn from_value<T: Schema>(v: &Value) -> Result<Read<T>, Vec<Diagnostic>> {
170 let arena = bumpalo::Bump::new();
171 read_with(&v.borrow(&arena), Reader::new())
172}
173
174pub fn read_value<T: Schema>(v: &Value) -> Result<T, Vec<Diagnostic>> {
177 let arena = bumpalo::Bump::new();
178 read_ref_value(&v.borrow(&arena))
179}
180
181pub fn read_ref_value<T: Schema>(v: &ValueRef) -> Result<T, Vec<Diagnostic>> {
183 let reader = Reader {
184 value_only: true,
185 ..Reader::new()
186 };
187 read_with(v, reader).map(|r| r.value)
188}
189
190pub fn read_bytes<T: Schema>(bytes: &[u8]) -> Result<T, Vec<Diagnostic>> {
193 if let Ok(mut d) = Decoder::open(bytes)
195 && let Some(v) = T::decode(&mut d)
196 && d.at_end()
197 {
198 return Ok(v);
199 }
200 let arena = bumpalo::Bump::new();
201 let v = crate::binary::decode_ref(bytes, &arena)
202 .map_err(|e| vec![Diagnostic::error(yog_ids::data_problem::BAD_BINARY, e)])?;
203 read_ref_value(&v)
204}
205
206pub fn from_value_with<T: Schema>(
208 v: &Value,
209 resolver: &mut dyn Resolve,
210) -> Result<Read<T>, Vec<Diagnostic>> {
211 let arena = bumpalo::Bump::new();
212 read_with(&v.borrow(&arena), Reader::with_resolver(resolver))
213}
214
215fn read_with<T: Schema>(v: &ValueRef, mut cx: Reader<'_>) -> Result<Read<T>, Vec<Diagnostic>> {
216 let value = T::read(v, &mut cx);
217 match value {
218 Some(value) if cx.problems.is_empty() => Ok(Read {
219 value,
220 refs: cx.refs,
221 tags: cx.tags,
222 resources: cx.resources,
223 }),
224 _ => Err(cx.problems),
225 }
226}
227
228pub fn built_in<T: Schema + Default>(what: &str, text: &str) -> T {
232 match from_text::<T>(text) {
233 Ok(read) => read.value,
234 Err(e) => {
235 yog_logging::error!("{what} won't read: {e}");
236 T::default()
237 }
238 }
239}
240
241pub fn from_text<T: Schema>(text: &str) -> Result<Read<T>, Errors> {
243 let doc = text::parse(text)?;
244 from_doc(text, &doc)
245}
246
247#[cfg(feature = "toml")]
249pub fn from_toml<T: Schema>(text: &str) -> Result<Read<T>, Errors> {
250 let doc = crate::toml::parse(text)?;
251 from_doc(text, &doc)
252}
253
254pub fn from_doc<T: Schema>(text: &str, doc: &Doc) -> Result<Read<T>, Errors> {
256 locate(text, doc, from_value(&doc.value))
257}
258
259pub fn from_doc_with<T: Schema>(
261 text: &str,
262 doc: &Doc,
263 resolver: &mut dyn Resolve,
264) -> Result<Read<T>, Errors> {
265 locate(text, doc, from_value_with(&doc.value, resolver))
266}
267
268fn locate<T>(
269 text: &str,
270 doc: &Doc,
271 read: Result<Read<T>, Vec<Diagnostic>>,
272) -> Result<Read<T>, Errors> {
273 read.map_err(|problems| {
274 let mut located: Vec<Diagnostic> =
275 problems.into_iter().map(|p| placed(text, doc, p)).collect();
276 located.sort_by_key(|d| (d.line, d.col));
278 Errors(located)
279 })
280}
281
282pub fn placed(text: &str, doc: &Doc, d: Diagnostic) -> Diagnostic {
285 let spans = doc.spans_at(&d.path);
286 let span = match (d.on_key, spans.key) {
287 (true, Some(key)) => key,
288 _ => spans.span,
289 };
290 let at = LineCol::of(text, span.start as usize);
291 d.at(at)
292}
293
294pub fn to_text<T: Schema>(value: &T) -> String {
296 text::to_string_pretty(&value.write())
297}