use alux_shape::{FieldAlg, ShapeAlg, Sorts, Words};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Term {
Truth,
Unit,
Text,
Literal(String),
NameWord(Vec<String>),
Int { signed: bool, bits: u16 },
Float { bits: u16 },
Bytes { len: Option<usize> },
Hex(Box<Term>),
Decimal(Box<Term>),
Base64(Box<Term>),
Opt(Box<Term>),
Seq(Box<Term>),
Map(Box<Term>, Box<Term>),
Product(Vec<Member>),
Choice(Vec<Term>),
Named { words: Vec<String>, body: Box<Term> },
Reference(Vec<String>),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Member {
Field { words: Vec<String>, shape: Term },
Merge(Term),
}
fn borrow(words: &[String]) -> Vec<&str> {
words.iter().map(String::as_str).collect()
}
fn own(words: Words<'_>) -> Vec<String> {
words.iter().map(|word| (*word).to_owned()).collect()
}
impl Term {
pub fn fold<A>(&self, alg: &A) -> A::Ty
where
A: ShapeAlg + FieldAlg,
{
match self {
Self::Truth => alg.truth(),
Self::Unit => alg.unit(),
Self::Text => alg.text(),
Self::Literal(text) => alg.literal(text),
Self::NameWord(words) => alg.name_word(&borrow(words)),
Self::Int { signed, bits } => alg.int(*signed, *bits),
Self::Float { bits } => alg.float(*bits),
Self::Bytes { len } => alg.bytes(*len),
Self::Hex(item) => alg.hex(item.fold(alg)),
Self::Decimal(item) => alg.decimal(item.fold(alg)),
Self::Base64(item) => alg.base64(item.fold(alg)),
Self::Opt(item) => alg.opt(item.fold(alg)),
Self::Seq(item) => alg.seq(item.fold(alg)),
Self::Map(key, value) => alg.map(key.fold(alg), value.fold(alg)),
Self::Product(members) => alg.product(members.iter().map(|member| member.fold(alg)).collect()),
Self::Choice(alternatives) => alg.choice(alternatives.iter().map(|item| item.fold(alg)).collect()),
Self::Named { words, body } => alg.named(&borrow(words), body.fold(alg)),
Self::Reference(words) => alg.reference(&borrow(words)),
}
}
#[must_use]
pub fn resolved(&self) -> Self {
let mut definitions = Vec::new();
self.definitions(&mut definitions);
self.expand(&definitions, &mut Vec::new())
}
fn definitions<'a>(&'a self, found: &mut Vec<(&'a [String], &'a Self)>) {
if let Self::Named { words, body } = self {
found.push((words, body));
}
self.children().for_each(|child| child.definitions(found));
}
fn expand(&self, definitions: &[(&[String], &Self)], expanding: &mut Vec<Vec<String>>) -> Self {
match self {
Self::Reference(words) => {
let known = definitions.iter().find(|(name, _)| *name == words.as_slice());
match known {
Some((_, body)) if !expanding.iter().any(|name| name == words) => {
expanding.push(words.clone());
let expanded = body.expand(definitions, expanding);
expanding.pop();
Self::Named { words: words.clone(), body: Box::new(expanded) }
}
_ => self.clone(),
}
}
Self::Hex(item) => Self::Hex(Box::new(item.expand(definitions, expanding))),
Self::Decimal(item) => Self::Decimal(Box::new(item.expand(definitions, expanding))),
Self::Base64(item) => Self::Base64(Box::new(item.expand(definitions, expanding))),
Self::Opt(item) => Self::Opt(Box::new(item.expand(definitions, expanding))),
Self::Seq(item) => Self::Seq(Box::new(item.expand(definitions, expanding))),
Self::Map(key, value) => {
Self::Map(Box::new(key.expand(definitions, expanding)), Box::new(value.expand(definitions, expanding)))
}
Self::Product(members) => {
Self::Product(members.iter().map(|member| member.expand(definitions, expanding)).collect())
}
Self::Choice(alternatives) => {
Self::Choice(alternatives.iter().map(|item| item.expand(definitions, expanding)).collect())
}
Self::Named { words, body } => {
expanding.push(words.clone());
let body = body.expand(definitions, expanding);
expanding.pop();
Self::Named { words: words.clone(), body: Box::new(body) }
}
leaf => leaf.clone(),
}
}
fn children(&self) -> Box<dyn Iterator<Item = &Self> + '_> {
match self {
Self::Hex(item)
| Self::Decimal(item)
| Self::Base64(item)
| Self::Opt(item)
| Self::Seq(item)
| Self::Named { body: item, .. } => Box::new(std::iter::once(&**item)),
Self::Map(key, value) => Box::new([&**key, &**value].into_iter()),
Self::Product(members) => Box::new(members.iter().map(Member::shape)),
Self::Choice(alternatives) => Box::new(alternatives.iter()),
_ => Box::new(std::iter::empty()),
}
}
}
impl Member {
fn shape(&self) -> &Term {
match self {
Self::Field { shape, .. } | Self::Merge(shape) => shape,
}
}
fn expand(&self, definitions: &[(&[String], &Term)], expanding: &mut Vec<Vec<String>>) -> Self {
match self {
Self::Field { words, shape } => {
Self::Field { words: words.clone(), shape: shape.expand(definitions, expanding) }
}
Self::Merge(shape) => Self::Merge(shape.expand(definitions, expanding)),
}
}
pub fn fold<A>(&self, alg: &A) -> A::Field
where
A: ShapeAlg + FieldAlg,
{
match self {
Self::Field { words, shape } => alg.field(&borrow(words), shape.fold(alg)),
Self::Merge(shape) => alg.merge(shape.fold(alg)),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct TermShape;
impl Sorts for TermShape {
type Ty = Term;
type Field = Member;
}
impl ShapeAlg for TermShape {
fn truth(&self) -> Term {
Term::Truth
}
fn unit(&self) -> Term {
Term::Unit
}
fn text(&self) -> Term {
Term::Text
}
fn literal(&self, text: &str) -> Term {
Term::Literal(text.to_owned())
}
fn name_word(&self, words: Words<'_>) -> Term {
Term::NameWord(own(words))
}
fn int(&self, signed: bool, bits: u16) -> Term {
Term::Int { signed, bits }
}
fn float(&self, bits: u16) -> Term {
Term::Float { bits }
}
fn bytes(&self, len: Option<usize>) -> Term {
Term::Bytes { len }
}
fn hex(&self, item: Term) -> Term {
Term::Hex(Box::new(item))
}
fn decimal(&self, item: Term) -> Term {
Term::Decimal(Box::new(item))
}
fn base64(&self, item: Term) -> Term {
Term::Base64(Box::new(item))
}
fn opt(&self, item: Term) -> Term {
Term::Opt(Box::new(item))
}
fn seq(&self, item: Term) -> Term {
Term::Seq(Box::new(item))
}
fn map(&self, key: Term, value: Term) -> Term {
Term::Map(Box::new(key), Box::new(value))
}
fn product(&self, fields: Vec<Member>) -> Term {
Term::Product(fields)
}
fn choice(&self, alternatives: Vec<Term>) -> Term {
Term::Choice(alternatives)
}
fn named(&self, words: Words<'_>, body: Term) -> Term {
Term::Named { words: own(words), body: Box::new(body) }
}
fn reference(&self, words: Words<'_>) -> Term {
Term::Reference(own(words))
}
}
impl FieldAlg for TermShape {
fn field(&self, words: Words<'_>, shape: Term) -> Member {
Member::Field { words: own(words), shape }
}
fn merge(&self, shape: Term) -> Member {
Member::Merge(shape)
}
}