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 fn to_bytes<T: Schema>(v: &T) -> Vec<u8> {
124 let mut w = Writer::new();
125 v.encode(&mut w);
126 w.finish()
127}
128
129pub fn field_names(shape: &Shape) -> Vec<&'static str> {
131 match shape {
132 Shape::Struct(s) => s.fields.iter().map(|f| f.name).collect(),
133 _ => Vec::new(),
134 }
135}
136
137pub fn read_both<A: Schema, B: Schema>(
140 v: &ValueRef,
141 cx: &mut Reader,
142 also: &[&'static str],
143) -> Option<(A, B)> {
144 let (an, bn) = (field_names(&A::shape()), field_names(&B::shape()));
145 let mut b_keys = bn.clone();
146 b_keys.extend_from_slice(also);
147 let mut a_keys = an;
148 a_keys.extend_from_slice(also);
149 let a = cx.with_allowed_all(&b_keys, |cx| A::read(v, cx));
150 let b = cx.with_allowed_all(&a_keys, |cx| B::read(v, cx));
151 Some((a?, b?))
152}
153
154pub fn from_value<T: Schema>(v: &Value) -> Result<Read<T>, Vec<Problem>> {
156 let arena = bumpalo::Bump::new();
157 read_with(&v.borrow(&arena), Reader::new())
158}
159
160pub fn read_value<T: Schema>(v: &Value) -> Result<T, Vec<Problem>> {
163 let arena = bumpalo::Bump::new();
164 read_ref_value(&v.borrow(&arena))
165}
166
167pub fn read_ref_value<T: Schema>(v: &ValueRef) -> Result<T, Vec<Problem>> {
169 let reader = Reader {
170 value_only: true,
171 ..Reader::new()
172 };
173 read_with(v, reader).map(|r| r.value)
174}
175
176pub fn read_bytes<T: Schema>(bytes: &[u8]) -> Result<T, Vec<Problem>> {
179 if let Ok(mut d) = Decoder::open(bytes)
181 && let Some(v) = T::decode(&mut d)
182 && d.at_end()
183 {
184 return Ok(v);
185 }
186 let arena = bumpalo::Bump::new();
187 let v = crate::binary::decode_ref(bytes, &arena).map_err(|e| {
188 vec![Problem {
189 path: Path::root(),
190 code: yog_ids::problem_code::BAD_BINARY,
191 message: e.to_string(),
192 at_key: false,
193 }]
194 })?;
195 read_ref_value(&v)
196}
197
198pub fn from_value_with<T: Schema>(
200 v: &Value,
201 resolver: &mut dyn Resolve,
202) -> Result<Read<T>, Vec<Problem>> {
203 let arena = bumpalo::Bump::new();
204 read_with(&v.borrow(&arena), Reader::with_resolver(resolver))
205}
206
207fn read_with<T: Schema>(v: &ValueRef, mut cx: Reader<'_>) -> Result<Read<T>, Vec<Problem>> {
208 let value = T::read(v, &mut cx);
209 match value {
210 Some(value) if cx.problems.is_empty() => Ok(Read {
211 value,
212 refs: cx.refs,
213 tags: cx.tags,
214 resources: cx.resources,
215 }),
216 _ => Err(cx.problems),
217 }
218}
219
220pub fn built_in<T: Schema + Default>(what: &str, text: &str) -> T {
224 match from_text::<T>(text) {
225 Ok(read) => read.value,
226 Err(e) => {
227 yog_logging::error!("{what} won't read: {e}");
228 T::default()
229 }
230 }
231}
232
233pub fn from_text<T: Schema>(text: &str) -> Result<Read<T>, Errors> {
235 let doc = text::parse(text).map_err(|e| {
236 Errors(vec![Located {
237 at: Some(e.at),
238 path: Path::root(),
239 message: e.message,
240 }])
241 })?;
242 from_doc(text, &doc)
243}
244
245#[cfg(feature = "toml")]
247pub fn from_toml<T: Schema>(text: &str) -> Result<Read<T>, Errors> {
248 let doc = crate::toml::parse(text).map_err(|e| {
249 Errors(vec![Located {
250 at: Some(e.at),
251 path: Path::root(),
252 message: e.message,
253 }])
254 })?;
255 from_doc(text, &doc)
256}
257
258pub fn from_doc<T: Schema>(text: &str, doc: &Doc) -> Result<Read<T>, Errors> {
260 locate(text, doc, from_value(&doc.value))
261}
262
263pub fn from_doc_with<T: Schema>(
265 text: &str,
266 doc: &Doc,
267 resolver: &mut dyn Resolve,
268) -> Result<Read<T>, Errors> {
269 locate(text, doc, from_value_with(&doc.value, resolver))
270}
271
272fn locate<T>(
273 text: &str,
274 doc: &Doc,
275 read: Result<Read<T>, Vec<Problem>>,
276) -> Result<Read<T>, Errors> {
277 read.map_err(|problems| {
278 let mut located: Vec<(u32, Located)> = problems
279 .into_iter()
280 .map(|p| {
281 let spans = doc.spans_at(&p.path);
282 let span = if p.at_key {
283 spans.key.unwrap_or(spans.span)
284 } else {
285 spans.span
286 };
287 (
288 span.start,
289 Located {
290 at: Some(LineCol::of(text, span.start as usize)),
291 path: p.path,
292 message: p.message,
293 },
294 )
295 })
296 .collect();
297 located.sort_by_key(|(offset, _)| *offset);
299 Errors(located.into_iter().map(|(_, l)| l).collect())
300 })
301}
302
303pub fn to_text<T: Schema>(value: &T) -> String {
305 text::to_string_pretty(&value.write())
306}