1mod fields;
8mod one_or_list;
9mod plain;
10mod reader;
11mod shape;
12mod typed_values;
13
14pub use fields::{Fields, field_name};
15pub use one_or_list::OneOrList;
16pub use plain::*;
17pub use reader::*;
18pub use shape::*;
19pub use typed_values::*;
20
21use std::collections::{BTreeMap, HashMap};
22use std::fmt;
23use std::marker::PhantomData;
24
25use crate::binary::{Decoder, Writer};
26use crate::borrowed::{MapRef, ValueRef};
27use crate::frame::{Packer, Unpacker};
28use crate::text::{self, Doc, LineCol};
29use crate::typed;
30use crate::value::{Map, Path, Ref, Res, Seg, Value};
31
32pub trait Schema: Sized {
34 fn shape() -> Shape;
36
37 fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self>;
40
41 fn write(&self) -> Value;
43
44 fn absent() -> Option<Self> {
46 None
47 }
48
49 fn skip(&self) -> bool {
51 false
52 }
53
54 fn encode(&self, w: &mut Writer) {
57 w.value(&self.write());
58 }
59
60 fn decode(d: &mut Decoder<'_>) -> Option<Self> {
65 let arena = bumpalo::Bump::new();
66 let v = d.value_ref(&arena).ok()?;
67 read_ref_value(&v).ok()
68 }
69
70 fn pack(&self, p: &mut Packer) {
73 p.put_tagged(self);
74 }
75
76 fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
79 u.get_tagged()
80 }
81
82 fn pack_list(items: &[Self], p: &mut Packer) {
84 let at = p.begin_list(items.len());
85 for item in items {
86 item.pack(p);
87 }
88 p.end(at);
89 }
90
91 fn unpack_list(u: &mut Unpacker<'_>) -> Option<Vec<Self>> {
93 let (n, end) = u.list()?;
94 let mut out = Vec::with_capacity(n);
95 for _ in 0..n {
96 out.push(Self::unpack(u)?);
97 }
98 u.skip_to(end)?;
99 Some(out)
100 }
101
102 fn encode_list(items: &[Self], w: &mut Writer) {
104 w.list(items.len());
105 for item in items {
106 item.encode(w);
107 }
108 }
109
110 fn decode_list(d: &mut Decoder<'_>) -> Option<Vec<Self>> {
112 let n = d.list()?;
113 let mut out = Vec::with_capacity(n);
114 for _ in 0..n {
115 out.push(Self::decode(d)?);
116 }
117 d.leave();
118 Some(out)
119 }
120}
121
122pub const WRAPPED: &str = yog_ids::word::VALUE;
126
127#[doc(hidden)]
130pub fn flattens<T: Schema>() -> bool {
131 matches!(T::shape(), Shape::Struct(_))
132}
133
134pub fn to_bytes<T: Schema>(v: &T) -> Vec<u8> {
136 let mut w = Writer::new();
137 v.encode(&mut w);
138 w.finish()
139}
140
141pub fn field_names(shape: &Shape) -> Vec<&'static str> {
143 match shape {
144 Shape::Struct(s) => s.fields.iter().map(|f| f.name).collect(),
145 _ => Vec::new(),
146 }
147}
148
149pub fn read_both<A: Schema, B: Schema>(
152 v: &ValueRef,
153 cx: &mut Reader,
154 also: &[&'static str],
155) -> Option<(A, B)> {
156 let (an, bn) = (field_names(&A::shape()), field_names(&B::shape()));
157 let mut b_keys = bn.clone();
158 b_keys.extend_from_slice(also);
159 let mut a_keys = an;
160 a_keys.extend_from_slice(also);
161 let a = cx.with_allowed_all(&b_keys, |cx| A::read(v, cx));
162 let b = cx.with_allowed_all(&a_keys, |cx| B::read(v, cx));
163 Some((a?, b?))
164}
165
166pub fn from_value<T: Schema>(v: &Value) -> Result<Read<T>, Vec<Problem>> {
168 let arena = bumpalo::Bump::new();
169 read_with(&v.borrow(&arena), Reader::new())
170}
171
172pub fn read_value<T: Schema>(v: &Value) -> Result<T, Vec<Problem>> {
175 let arena = bumpalo::Bump::new();
176 read_ref_value(&v.borrow(&arena))
177}
178
179pub fn read_ref_value<T: Schema>(v: &ValueRef) -> Result<T, Vec<Problem>> {
181 let reader = Reader {
182 value_only: true,
183 ..Reader::new()
184 };
185 read_with(v, reader).map(|r| r.value)
186}
187
188pub fn read_bytes<T: Schema>(bytes: &[u8]) -> Result<T, Vec<Problem>> {
191 if let Ok(mut d) = Decoder::open(bytes)
193 && let Some(v) = T::decode(&mut d)
194 && d.at_end()
195 {
196 return Ok(v);
197 }
198 let arena = bumpalo::Bump::new();
199 let v = crate::binary::decode_ref(bytes, &arena).map_err(|e| {
200 vec![Problem {
201 path: Path::root(),
202 code: yog_ids::problem_code::BAD_BINARY,
203 message: e.to_string(),
204 at_key: false,
205 }]
206 })?;
207 read_ref_value(&v)
208}
209
210pub fn from_value_with<T: Schema>(
212 v: &Value,
213 resolver: &mut dyn Resolve,
214) -> Result<Read<T>, Vec<Problem>> {
215 let arena = bumpalo::Bump::new();
216 read_with(&v.borrow(&arena), Reader::with_resolver(resolver))
217}
218
219fn read_with<T: Schema>(v: &ValueRef, mut cx: Reader<'_>) -> Result<Read<T>, Vec<Problem>> {
220 let value = T::read(v, &mut cx);
221 match value {
222 Some(value) if cx.problems.is_empty() => Ok(Read {
223 value,
224 refs: cx.refs,
225 tags: cx.tags,
226 resources: cx.resources,
227 }),
228 _ => Err(cx.problems),
229 }
230}
231
232pub fn built_in<T: Schema + Default>(what: &str, text: &str) -> T {
236 match from_text::<T>(text) {
237 Ok(read) => read.value,
238 Err(e) => {
239 yog_logging::error!("{what} won't read: {e}");
240 T::default()
241 }
242 }
243}
244
245pub fn from_text<T: Schema>(text: &str) -> Result<Read<T>, Errors> {
247 let doc = text::parse(text).map_err(|e| {
248 Errors(vec![Located {
249 at: Some(e.at),
250 path: Path::root(),
251 message: e.message,
252 }])
253 })?;
254 from_doc(text, &doc)
255}
256
257#[cfg(feature = "toml")]
259pub fn from_toml<T: Schema>(text: &str) -> Result<Read<T>, Errors> {
260 let doc = crate::toml::parse(text).map_err(|e| {
261 Errors(vec![Located {
262 at: Some(e.at),
263 path: Path::root(),
264 message: e.message,
265 }])
266 })?;
267 from_doc(text, &doc)
268}
269
270pub fn from_doc<T: Schema>(text: &str, doc: &Doc) -> Result<Read<T>, Errors> {
272 locate(text, doc, from_value(&doc.value))
273}
274
275pub fn from_doc_with<T: Schema>(
277 text: &str,
278 doc: &Doc,
279 resolver: &mut dyn Resolve,
280) -> Result<Read<T>, Errors> {
281 locate(text, doc, from_value_with(&doc.value, resolver))
282}
283
284fn locate<T>(
285 text: &str,
286 doc: &Doc,
287 read: Result<Read<T>, Vec<Problem>>,
288) -> Result<Read<T>, Errors> {
289 read.map_err(|problems| {
290 let mut located: Vec<(u32, Located)> = problems
291 .into_iter()
292 .map(|p| {
293 let spans = doc.spans_at(&p.path);
294 let span = if p.at_key {
295 spans.key.unwrap_or(spans.span)
296 } else {
297 spans.span
298 };
299 (
300 span.start,
301 Located {
302 at: Some(LineCol::of(text, span.start as usize)),
303 path: p.path,
304 message: p.message,
305 },
306 )
307 })
308 .collect();
309 located.sort_by_key(|(offset, _)| *offset);
311 Errors(located.into_iter().map(|(_, l)| l).collect())
312 })
313}
314
315pub fn to_text<T: Schema>(value: &T) -> String {
317 text::to_string_pretty(&value.write())
318}