use std::collections::{BTreeMap, HashMap};
use std::marker::PhantomData;
use crate::binary::{Decoder, Writer};
use crate::borrowed::ValueRef;
use crate::frame::{Packer, Unpacker};
use crate::schema::{Kinds, Reader, Schema, Shape, Text, is_metadata_key, read_ref};
use crate::value::{Map, Ref, Seg, Value};
pub trait With<F> {
fn shape() -> Shape;
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<F>;
fn write(f: &F) -> Value;
fn absent() -> Option<F> {
None
}
fn skip(_f: &F) -> bool {
false
}
fn encode(f: &F, w: &mut Writer) {
w.value(&Self::write(f));
}
fn decode(d: &mut Decoder<'_>) -> Option<F> {
let arena = bumpalo::Bump::new();
let v = d.value_ref(&arena).ok()?;
let mut cx = Reader::new();
let f = Self::read(&v, &mut cx)?;
cx.problems.is_empty().then_some(f)
}
fn pack(f: &F, p: &mut Packer) {
let mut w = Writer::body();
Self::encode(f, &mut w);
p.put_bytes(&w.finish());
}
fn unpack(u: &mut Unpacker<'_>) -> Option<F> {
let mut d = Decoder::body(u.get_bytes()?);
let f = Self::decode(&mut d)?;
d.at_end().then_some(f)
}
}
pub struct RefTo<K>(PhantomData<K>);
impl<K: Kinds> With<String> for RefTo<K> {
fn shape() -> Shape {
Shape::Ref(K::NAMES)
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<String> {
if let ValueRef::Ref { kind, id } = v
&& (K::NAMES.is_empty() || K::NAMES.contains(kind))
{
if cx.keeps_found() || cx.resolves() {
let r = Ref::new(*kind, *id);
if cx.keeps_found() {
cx.note_ref(r.clone());
}
return cx.take_ref(r);
}
return Some(id.to_string());
}
let r = read_ref(v, cx, K::NAMES)?;
cx.take_ref(r)
}
fn write(f: &String) -> Value {
Value::Ref(Ref::new(
K::NAMES.first().copied().unwrap_or(yog_ids::kind::ITEM),
f.clone(),
))
}
}
pub struct Spelled;
impl With<String> for Spelled {
fn shape() -> Shape {
Shape::Str
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<String> {
if let ValueRef::Str(s) = v {
return Some(s.to_string());
}
let spelled = crate::typed::typed_string(&v.to_value());
if spelled.is_none() {
cx.expected("text", v);
}
spelled
}
fn write(f: &String) -> Value {
Value::Str(f.clone())
}
}
pub struct TextOf;
impl With<String> for TextOf {
fn shape() -> Shape {
Shape::Text
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<String> {
let t = Text::read(v, cx)?;
cx.take_text(t)
}
fn write(f: &String) -> Value {
Value::Text(f.clone())
}
}
pub struct Opt<W>(PhantomData<W>);
impl<F, W: With<F>> With<Option<F>> for Opt<W> {
fn shape() -> Shape {
Shape::Optional(W::shape)
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Option<F>> {
if v.is_null() {
Some(None)
} else {
W::read(v, cx).map(Some)
}
}
fn write(f: &Option<F>) -> Value {
f.as_ref().map_or(Value::Null, W::write)
}
fn absent() -> Option<Option<F>> {
Some(None)
}
fn skip(f: &Option<F>) -> bool {
f.is_none()
}
}
pub struct List<W>(PhantomData<W>);
impl<F, W: With<F>> With<Vec<F>> for List<W> {
fn shape() -> Shape {
Shape::List(W::shape)
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Vec<F>> {
let ValueRef::List(items) = v else {
cx.expected("a list", v);
return None;
};
let mut out = Vec::with_capacity(items.len());
let mut ok = true;
for (i, item) in items.iter().enumerate() {
match cx.at(Seg::Index(i), |cx| W::read(item, cx)) {
Some(x) => out.push(x),
None => ok = false,
}
}
ok.then_some(out)
}
fn write(f: &Vec<F>) -> Value {
Value::List(f.iter().map(W::write).collect())
}
}
pub struct Values<W>(PhantomData<W>);
impl<F, W: With<F>> With<BTreeMap<String, F>> for Values<W> {
fn shape() -> Shape {
Shape::Map(W::shape)
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<BTreeMap<String, F>> {
read_values::<F, W, _>(v, cx)
}
fn write(f: &BTreeMap<String, F>) -> Value {
write_values::<F, W>(f.iter())
}
}
impl<F, W: With<F>> With<HashMap<String, F>> for Values<W> {
fn shape() -> Shape {
Shape::Map(W::shape)
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<HashMap<String, F>> {
read_values::<F, W, _>(v, cx)
}
fn write(f: &HashMap<String, F>) -> Value {
write_values::<F, W>(f.iter())
}
}
fn read_values<F, W: With<F>, M: Default + Extend<(String, F)>>(
v: &ValueRef<'_>,
cx: &mut Reader,
) -> Option<M> {
let map = cx.map(v)?;
let mut out = M::default();
let mut ok = true;
for (k, item) in map.iter().filter(|(k, _)| !is_metadata_key(k)) {
match cx.at(Seg::Key(k.to_string()), |cx| W::read(item, cx)) {
Some(x) => out.extend([(k.to_string(), x)]),
None => ok = false,
}
}
ok.then_some(out)
}
fn write_values<'a, F: 'a, W: With<F>>(
entries: impl Iterator<Item = (&'a String, &'a F)>,
) -> Value {
Value::Map(
entries
.map(|(k, v)| (k.clone(), W::write(v)))
.collect::<Map>(),
)
}
pub struct Plain;
impl<F: Schema> With<F> for Plain {
fn shape() -> Shape {
F::shape()
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<F> {
F::read(v, cx)
}
fn write(f: &F) -> Value {
f.write()
}
fn absent() -> Option<F> {
F::absent()
}
fn skip(f: &F) -> bool {
f.skip()
}
fn encode(f: &F, w: &mut Writer) {
f.encode(w);
}
fn decode(d: &mut Decoder<'_>) -> Option<F> {
F::decode(d)
}
fn pack(f: &F, p: &mut Packer) {
f.pack(p);
}
fn unpack(u: &mut Unpacker<'_>) -> Option<F> {
F::unpack(u)
}
}
pub struct Raw;
impl With<Vec<u8>> for Raw {
fn shape() -> Shape {
Shape::Bytes
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Vec<u8>> {
match v {
ValueRef::Bytes(b) => Some(b.to_vec()),
ValueRef::Str(s) => match crate::typed::classify(s).map_err(|e| cx.problem(e)).ok()? {
crate::Value::Bytes(b) => Some(b),
_ => {
cx.expected("bytes", v);
None
}
},
other => {
cx.expected("bytes", other);
None
}
}
}
fn write(f: &Vec<u8>) -> Value {
Value::Bytes(f.clone())
}
fn encode(f: &Vec<u8>, w: &mut Writer) {
w.bytes(f);
}
fn decode(d: &mut Decoder<'_>) -> Option<Vec<u8>> {
d.bytes().map(<[u8]>::to_vec)
}
fn pack(f: &Vec<u8>, p: &mut Packer) {
p.put_bytes(f);
}
fn unpack(u: &mut Unpacker<'_>) -> Option<Vec<u8>> {
u.get_bytes().map(<[u8]>::to_vec)
}
fn absent() -> Option<Vec<u8>> {
Some(Vec::new())
}
}