mod fields;
mod one_or_list;
mod plain;
mod reader;
mod shape;
mod typed_values;
pub use fields::{Fields, field_name};
pub use one_or_list::OneOrList;
pub use plain::*;
pub use reader::*;
pub use shape::*;
pub use typed_values::*;
use std::collections::{BTreeMap, HashMap};
use std::fmt;
use std::marker::PhantomData;
use crate::binary::{Decoder, Writer};
use crate::borrowed::{MapRef, ValueRef};
use crate::frame::{Packer, Unpacker};
use crate::text::{self, Doc, LineCol};
use crate::typed;
use crate::value::{Map, Path, Ref, Res, Seg, Value};
pub trait Schema: Sized {
fn shape() -> Shape;
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self>;
fn write(&self) -> Value;
fn absent() -> Option<Self> {
None
}
fn skip(&self) -> bool {
false
}
fn encode(&self, w: &mut Writer) {
w.value(&self.write());
}
fn decode(d: &mut Decoder<'_>) -> Option<Self> {
let arena = bumpalo::Bump::new();
let v = d.value_ref(&arena).ok()?;
read_ref_value(&v).ok()
}
fn pack(&self, p: &mut Packer) {
p.put_tagged(self);
}
fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
u.get_tagged()
}
fn pack_list(items: &[Self], p: &mut Packer) {
let at = p.begin_list(items.len());
for item in items {
item.pack(p);
}
p.end(at);
}
fn unpack_list(u: &mut Unpacker<'_>) -> Option<Vec<Self>> {
let (n, end) = u.list()?;
let mut out = Vec::with_capacity(n);
for _ in 0..n {
out.push(Self::unpack(u)?);
}
u.skip_to(end)?;
Some(out)
}
fn encode_list(items: &[Self], w: &mut Writer) {
w.list(items.len());
for item in items {
item.encode(w);
}
}
fn decode_list(d: &mut Decoder<'_>) -> Option<Vec<Self>> {
let n = d.list()?;
let mut out = Vec::with_capacity(n);
for _ in 0..n {
out.push(Self::decode(d)?);
}
d.leave();
Some(out)
}
}
pub fn to_bytes<T: Schema>(v: &T) -> Vec<u8> {
let mut w = Writer::new();
v.encode(&mut w);
w.finish()
}
pub fn field_names(shape: &Shape) -> Vec<&'static str> {
match shape {
Shape::Struct(s) => s.fields.iter().map(|f| f.name).collect(),
_ => Vec::new(),
}
}
pub fn read_both<A: Schema, B: Schema>(
v: &ValueRef,
cx: &mut Reader,
also: &[&'static str],
) -> Option<(A, B)> {
let (an, bn) = (field_names(&A::shape()), field_names(&B::shape()));
let mut b_keys = bn.clone();
b_keys.extend_from_slice(also);
let mut a_keys = an;
a_keys.extend_from_slice(also);
let a = cx.with_allowed_all(&b_keys, |cx| A::read(v, cx));
let b = cx.with_allowed_all(&a_keys, |cx| B::read(v, cx));
Some((a?, b?))
}
pub fn from_value<T: Schema>(v: &Value) -> Result<Read<T>, Vec<Problem>> {
let arena = bumpalo::Bump::new();
read_with(&v.borrow(&arena), Reader::new())
}
pub fn read_value<T: Schema>(v: &Value) -> Result<T, Vec<Problem>> {
let arena = bumpalo::Bump::new();
read_ref_value(&v.borrow(&arena))
}
pub fn read_ref_value<T: Schema>(v: &ValueRef) -> Result<T, Vec<Problem>> {
let reader = Reader {
value_only: true,
..Reader::new()
};
read_with(v, reader).map(|r| r.value)
}
pub fn read_bytes<T: Schema>(bytes: &[u8]) -> Result<T, Vec<Problem>> {
if let Ok(mut d) = Decoder::open(bytes)
&& let Some(v) = T::decode(&mut d)
&& d.at_end()
{
return Ok(v);
}
let arena = bumpalo::Bump::new();
let v = crate::binary::decode_ref(bytes, &arena).map_err(|e| {
vec![Problem {
path: Path::root(),
code: yog_ids::problem_code::BAD_BINARY,
message: e.to_string(),
at_key: false,
}]
})?;
read_ref_value(&v)
}
pub fn from_value_with<T: Schema>(
v: &Value,
resolver: &mut dyn Resolve,
) -> Result<Read<T>, Vec<Problem>> {
let arena = bumpalo::Bump::new();
read_with(&v.borrow(&arena), Reader::with_resolver(resolver))
}
fn read_with<T: Schema>(v: &ValueRef, mut cx: Reader<'_>) -> Result<Read<T>, Vec<Problem>> {
let value = T::read(v, &mut cx);
match value {
Some(value) if cx.problems.is_empty() => Ok(Read {
value,
refs: cx.refs,
tags: cx.tags,
resources: cx.resources,
}),
_ => Err(cx.problems),
}
}
pub fn built_in<T: Schema + Default>(what: &str, text: &str) -> T {
match from_text::<T>(text) {
Ok(read) => read.value,
Err(e) => {
yog_logging::error!("{what} won't read: {e}");
T::default()
}
}
}
pub fn from_text<T: Schema>(text: &str) -> Result<Read<T>, Errors> {
let doc = text::parse(text).map_err(|e| {
Errors(vec![Located {
at: Some(e.at),
path: Path::root(),
message: e.message,
}])
})?;
from_doc(text, &doc)
}
#[cfg(feature = "toml")]
pub fn from_toml<T: Schema>(text: &str) -> Result<Read<T>, Errors> {
let doc = crate::toml::parse(text).map_err(|e| {
Errors(vec![Located {
at: Some(e.at),
path: Path::root(),
message: e.message,
}])
})?;
from_doc(text, &doc)
}
pub fn from_doc<T: Schema>(text: &str, doc: &Doc) -> Result<Read<T>, Errors> {
locate(text, doc, from_value(&doc.value))
}
pub fn from_doc_with<T: Schema>(
text: &str,
doc: &Doc,
resolver: &mut dyn Resolve,
) -> Result<Read<T>, Errors> {
locate(text, doc, from_value_with(&doc.value, resolver))
}
fn locate<T>(
text: &str,
doc: &Doc,
read: Result<Read<T>, Vec<Problem>>,
) -> Result<Read<T>, Errors> {
read.map_err(|problems| {
let mut located: Vec<(u32, Located)> = problems
.into_iter()
.map(|p| {
let spans = doc.spans_at(&p.path);
let span = if p.at_key {
spans.key.unwrap_or(spans.span)
} else {
spans.span
};
(
span.start,
Located {
at: Some(LineCol::of(text, span.start as usize)),
path: p.path,
message: p.message,
},
)
})
.collect();
located.sort_by_key(|(offset, _)| *offset);
Errors(located.into_iter().map(|(_, l)| l).collect())
})
}
pub fn to_text<T: Schema>(value: &T) -> String {
text::to_string_pretty(&value.write())
}