use super::forms::{hex, key, named, nearest, unhex};
use super::value::{Map, Value};
use super::{Error, ErrorKind};
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
pub trait Tree: Sized {
const NAME: &'static str;
fn to_tree(&self) -> Value;
fn from_tree(value: &Value, reading: &mut Reading) -> Result<Self, Refused>;
}
#[derive(Debug)]
pub struct Refused;
pub fn to_value<T: Tree>(value: &T) -> Value {
value.to_tree()
}
pub fn from_value<T: Tree>(value: &Value) -> Result<T, Error> {
let mut reading = Reading::new();
match T::from_tree(value, &mut reading) {
Ok(value) => Ok(value),
Err(Refused) => Err(reading.refused(T::NAME)),
}
}
fn written(value: &Value) -> String {
let text = value.to_string();
match text.char_indices().nth(40) {
Some((end, _)) => format!("{}…", &text[..end]),
None => text,
}
}
fn some_of(names: &[&str], name: &str) -> String {
const ALL: usize = 12;
if names.len() <= ALL {
return names.join(", ");
}
let count = names.len();
match nearest(names, name, 3)[..] {
[] => format!("{}, and {} more", names[..8].join(", "), count - 8),
[one] => format!("the nearest of its {count} is {one}"),
[ref near @ .., last] => format!(
"the nearest of its {count} are {} and {last}",
near.join(", ")
),
}
}
#[derive(Debug, Default)]
pub struct Members(Map);
impl Members {
pub fn new() -> Self {
Self(Map::new())
}
pub fn put(&mut self, name: &str, value: Value) {
self.0.insert(name.to_string(), value);
}
pub fn put_optional(&mut self, name: &str, value: Option<Value>) {
if let Some(value) = value {
self.put(name, value);
}
}
pub fn finish(self) -> Value {
Value::Object(self.0)
}
}
#[derive(Debug, Clone, Copy)]
enum Name {
Member(&'static str),
Entry(usize),
}
#[derive(Debug)]
pub struct Reading {
at: Vec<Name>,
refusal: Option<(ErrorKind, String, String)>,
secret: bool,
}
#[derive(Debug)]
pub struct Read<'v> {
names: &'static [&'static str],
values: Vec<Option<&'v Value>>,
}
impl Reading {
fn new() -> Self {
Self {
at: Vec::new(),
refusal: None,
secret: false,
}
}
fn refused(self, name: &str) -> Error {
let (kind, at, why) = self.refusal.unwrap_or_else(|| {
let why = "it is refused without a reason".to_string();
(ErrorKind::Value, String::new(), why)
});
Error::new(kind, at, format!("this is not a value of {name}: {why}"))
}
pub fn refuse(&mut self, kind: ErrorKind, why: impl Into<String>) -> Refused {
if self.refusal.is_none() {
let segment = |name: &Name| match name {
Name::Member(name) => name.to_string(),
Name::Entry(position) => position.to_string(),
};
let at: Vec<String> = self.at.iter().map(segment).collect();
self.refusal = Some((kind, at.join("/"), why.into()));
}
Refused
}
pub fn mismatch(&mut self, expected: &str, found: &Value) -> Refused {
let why = format!(
"{expected} is expected, and {} is written",
self.shown(found)
);
self.refuse(ErrorKind::Value, why)
}
pub fn secret<T>(
&mut self,
read: impl FnOnce(&mut Self) -> Result<T, Refused>,
) -> Result<T, Refused> {
let was = core::mem::replace(&mut self.secret, true);
let read = read(self);
self.secret = was;
read
}
pub fn shown(&self, value: &Value) -> String {
match self.secret {
true => "a secret value".to_string(),
false => written(value),
}
}
pub fn members<'v>(
&mut self,
value: &'v Value,
names: &'static [&'static str],
) -> Result<Read<'v>, Refused> {
let Some(members) = value.as_object() else {
return Err(self.mismatch("an object", value));
};
let mut values = alloc::vec![None; names.len()];
let mut found = alloc::vec![false; names.len()];
for (written, held) in members {
let exact = names.iter().position(|name| *name == written);
let Some(position) = exact.or_else(|| named(names, written)) else {
let why = format!(
"{written:?} is not a member that its type has: it has {}",
names.join(", ")
);
return Err(self.refuse(ErrorKind::NoSuchMember, why));
};
if found[position] {
let why = format!("the member {:?} is written twice", names[position]);
return Err(self.refuse(ErrorKind::Value, why));
}
found[position] = true;
values[position] = Some(held).filter(|held| !held.is_null());
}
Ok(Read { names, values })
}
pub fn member<'v, T>(
&mut self,
of: &Read<'v>,
position: usize,
read: impl FnOnce(&'v Value, &mut Self) -> Result<T, Refused>,
) -> Result<T, Refused> {
self.at.push(Name::Member(of.names[position]));
let Some(value) = of.values[position] else {
return Err(self.refuse(ErrorKind::Value, "the member is missing"));
};
let value = read(value, self)?;
self.at.pop();
Ok(value)
}
pub fn optional<'v, T>(
&mut self,
of: &Read<'v>,
position: usize,
read: impl FnOnce(&'v Value, &mut Self) -> Result<T, Refused>,
) -> Result<Option<T>, Refused> {
let Some(value) = of.values[position] else {
return Ok(None);
};
self.at.push(Name::Member(of.names[position]));
let value = read(value, self)?;
self.at.pop();
Ok(Some(value))
}
pub fn list<'v, T>(
&mut self,
value: &'v Value,
mut read: impl FnMut(&'v Value, &mut Self) -> Result<T, Refused>,
) -> Result<Vec<T>, Refused> {
let Some(entries) = value.as_array() else {
return Err(self.mismatch("a list", value));
};
let mut values = Vec::with_capacity(entries.len());
for (position, entry) in entries.iter().enumerate() {
self.at.push(Name::Entry(position));
values.push(read(entry, self)?);
self.at.pop();
}
Ok(values)
}
pub fn alternative<'v>(
&mut self,
value: &'v Value,
names: &'static [&'static str],
) -> Result<(usize, &'v Value), Refused> {
const ONE: &str = "an object with one member, the name of an alternative with its value,";
let mut members = value.as_object().into_iter().flatten();
let (Some((written, held)), None) = (members.next(), members.next()) else {
return Err(self.mismatch(ONE, value));
};
let Some(position) = named(names, written) else {
let why = format!(
"{written:?} is not an alternative that its type has: it has {}",
names.join(", ")
);
return Err(self.refuse(ErrorKind::NoSuchMember, why));
};
Ok((position, held))
}
pub fn within<'v, T>(
&mut self,
name: &'static str,
value: &'v Value,
read: impl FnOnce(&'v Value, &mut Self) -> Result<T, Refused>,
) -> Result<T, Refused> {
self.at.push(Name::Member(name));
let value = read(value, self)?;
self.at.pop();
Ok(value)
}
pub fn boolean(&mut self, value: &Value) -> Result<bool, Refused> {
match value.as_bool() {
Some(read) => Ok(read),
None => Err(self.mismatch("true or false", value)),
}
}
pub fn null(&mut self, value: &Value) -> Result<(), Refused> {
match value.is_null() {
true => Ok(()),
false => Err(self.mismatch("null", value)),
}
}
pub fn text<'v>(&mut self, value: &'v Value) -> Result<&'v str, Refused> {
match value.as_str() {
Some(text) => Ok(text),
None => Err(self.mismatch("a string", value)),
}
}
pub fn named(&mut self, value: &Value, names: &[&str]) -> Result<usize, Refused> {
let text = match value {
Value::Bool(true) if names.contains(&"true") => "true",
Value::Bool(false) if names.contains(&"false") => "false",
_ => match value.as_str() {
Some(text) => text,
None => return Err(self.mismatch("a name", value)),
},
};
named(names, text).ok_or_else(|| {
let why = format!("{text:?} is none of its names: {}", some_of(names, text));
self.refuse(ErrorKind::Value, why)
})
}
pub fn unsigned(&mut self, value: &Value, lower: u64, upper: u64) -> Result<u64, Refused> {
let number = self.number(value, lower.into(), upper.into())?;
match u64::try_from(number) {
Ok(number) if (lower..=upper).contains(&number) => Ok(number),
_ => Err(self.outside(value, lower.into(), upper.into())),
}
}
pub fn signed(&mut self, value: &Value, lower: i64, upper: i64) -> Result<i64, Refused> {
let number = self.number(value, lower.into(), upper.into())?;
match i64::try_from(number) {
Ok(number) if (lower..=upper).contains(&number) => Ok(number),
_ => Err(self.outside(value, lower.into(), upper.into())),
}
}
fn number(&mut self, value: &Value, lower: i128, upper: i128) -> Result<i128, Refused> {
let read = value.as_number().and_then(|number| {
let signed = number.as_i64().map(i128::from);
signed.or_else(|| number.as_u64().map(i128::from))
});
read.ok_or_else(|| self.outside(value, lower, upper))
}
fn outside(&mut self, value: &Value, lower: i128, upper: i128) -> Refused {
self.mismatch(&format!("a number from {lower} to {upper}"), value)
}
pub fn octets(&mut self, value: &Value) -> Result<Vec<u8>, Refused> {
match value.as_str().and_then(unhex) {
Some(octets) => Ok(octets),
None => {
let why = format!(
"octets in hexadecimal, two digits for each, are expected, and {} is written",
self.shown(value)
);
Err(self.refuse(ErrorKind::Value, why))
}
}
}
pub fn octet_array<const N: usize>(&mut self, value: &Value) -> Result<[u8; N], Refused> {
let octets = self.octets(value)?;
<[u8; N]>::try_from(octets).map_err(|octets| {
let written = match self.secret {
true => "what is written".to_string(),
false => hex(&octets),
};
let why = format!(
"{N} octets are expected, and {written} has {}",
octets.len()
);
self.refuse(ErrorKind::Value, why)
})
}
pub fn bits(&mut self, value: &Value) -> Result<(Vec<u8>, usize), Refused> {
let Some(members) = value.as_object() else {
let octets = self.octets(value)?;
let length = octets.len() * 8;
return Ok((octets, length));
};
let _ = members;
let of = self.members(value, &["value", "length"])?;
let octets = self.member(&of, 0, |value, reading| reading.octets(value))?;
let length = self.member(&of, 1, |value, reading| {
reading.unsigned(value, 0, usize::MAX as u64)
})? as usize;
if length.div_ceil(8) != octets.len() {
let why = format!("{length} bits are not {} octets", octets.len());
return Err(self.refuse(ErrorKind::Value, why));
}
let unused = octets.len() * 8 - length;
if octets
.last()
.is_some_and(|last| last & ((1_u16 << unused) - 1) as u8 != 0)
{
let why = format!("the bits after the {length} of the string are not zero");
return Err(self.refuse(ErrorKind::Value, why));
}
Ok((octets, length))
}
}
pub fn member<'m>(members: &'m Map, name: &str) -> Option<(&'m str, &'m Value)> {
let known = key(members, name)?;
Some((known, &members[known]))
}