use std::fmt;
use std::fmt::{Display, Formatter};
use std::ops::Range;
use amplify::num::{u24, u40, u48, u56};
use encoding::{Ident, Sizing};
use vesper::{AttrVal, Attribute, Expression, Predicate, TExpr};
use crate::Cls;
pub type TypeVesper = TExpr<Pred>;
#[derive(Clone, Eq, PartialEq, Hash, Debug, Display)]
#[display(lowercase)]
pub enum Pred {
Field,
Tuple,
Struct,
Enum,
Union,
Char,
Str,
Ascii,
Bytes,
Array,
List,
Set,
Map,
}
impl Predicate for Pred {
type Attr = Attr;
}
impl From<Cls> for Pred {
fn from(cls: Cls) -> Self {
match cls {
Cls::Primitive => Pred::Field,
Cls::Unicode => Pred::Str,
Cls::AsciiStr => Pred::Ascii,
Cls::Enum => Pred::Enum,
Cls::Union => Pred::Union,
Cls::Struct => Pred::Struct,
Cls::Tuple => Pred::Tuple,
Cls::Array => Pred::Array,
Cls::List => Pred::List,
Cls::Set => Pred::Set,
Cls::Map => Pred::Map,
}
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug, Display)]
#[display(inner)]
pub enum AttrExpr {
Tag(u8),
EnumVariant(u8),
Len(u16),
LenRange(LenRange),
}
impl Expression for AttrExpr {}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub enum Attr {
TypeName(Ident),
Wrapped(Option<Ident>),
Option,
Tag(u8),
AsciiEnum(Ident),
AsciiFirst(Ident),
AsciiRest(Ident),
EnumVariant(u8, Ident),
Len(u16),
LenRange(LenRange),
}
impl Attribute for Attr {
type Expression = AttrExpr;
fn name(&self) -> Option<Ident> {
match self {
Attr::TypeName(_) => None,
Attr::Wrapped(name) if name.is_some() => Some(ident!("alias")),
Attr::Wrapped(_) => None,
Attr::Option => None,
Attr::Tag(_) => Some(ident!("tag")),
Attr::Len(_) => Some(ident!("len")),
Attr::LenRange(_) => Some(ident!("len")),
Attr::AsciiEnum(_) => Some(ident!("charset")),
Attr::AsciiFirst(_) => Some(ident!("first")),
Attr::AsciiRest(_) => Some(ident!("rest")),
Attr::EnumVariant(_, name) => Some(name.clone()),
}
}
fn value(&self) -> AttrVal<Self::Expression> {
match self {
Attr::TypeName(tn) => AttrVal::Ident(tn.clone()),
Attr::Wrapped(name) => AttrVal::Ident(name.clone().unwrap_or(ident!("wrapped"))),
Attr::Option => AttrVal::Ident(ident!("option")),
Attr::Tag(tag) => AttrVal::Expr(AttrExpr::Tag(*tag)),
Attr::Len(len) => AttrVal::Expr(AttrExpr::Len(*len)),
Attr::LenRange(range) => AttrVal::Expr(AttrExpr::LenRange(range.clone())),
Attr::AsciiEnum(name) => AttrVal::Ident(name.clone()),
Attr::AsciiFirst(name) => AttrVal::Ident(name.clone()),
Attr::AsciiRest(name) => AttrVal::Ident(name.clone()),
Attr::EnumVariant(pos, _) => AttrVal::Expr(AttrExpr::Tag(*pos)),
}
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct LenRange(Range<u64>);
impl From<Sizing> for LenRange {
#[inline]
fn from(sizing: Sizing) -> Self { Self(sizing.min..sizing.max) }
}
impl Display for LenRange {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.0.start {
min if min < 16 => write!(f, "{min}")?,
min => write!(f, "{min:X}.h")?,
}
f.write_str("..")?;
match self.0.end {
max if max == u16::MAX as u64 => f.write_str("<2^16"),
max if max == u24::MAX.into_u64() => f.write_str("<2^24"),
max if max == u32::MAX as u64 => f.write_str("<2^32"),
max if max == u40::MAX.into_u64() => f.write_str("<2^40"),
max if max == u48::MAX.into_u64() => f.write_str("<2^48"),
max if max == u56::MAX.into_u64() => f.write_str("<2^56"),
u64::MAX => f.write_str("<2^64"),
max if max < 16 => write!(f, "{max}"),
max => write!(f, "={max:X}.h"),
}
}
}