use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
use super::{
ComplexInfo, CustomType, DynamicInfo, EnumerationInfo, GroupInfo, ReferenceInfo, Types,
UnionInfo,
};
#[derive(Debug, Clone)]
pub struct Type {
pub display_name: Option<String>,
pub variant: TypeVariant,
}
#[derive(Debug, Clone)]
pub enum TypeVariant {
Union(UnionInfo),
BuildIn(BuildInInfo),
Reference(ReferenceInfo),
Enumeration(EnumerationInfo),
Dynamic(DynamicInfo),
All(GroupInfo),
Choice(GroupInfo),
Sequence(GroupInfo),
ComplexType(ComplexInfo),
}
pub trait TypeEq: Sized {
fn type_hash<H: Hasher>(&self, hasher: &mut H, types: &Types);
fn type_hash_slice<H: Hasher>(slice: &[Self], hasher: &mut H, types: &Types) {
hasher.write_usize(slice.len());
for item in slice {
item.type_hash(hasher, types);
}
}
fn type_eq(&self, other: &Self, types: &Types) -> bool;
fn type_eq_iter<'a, X, Y>(x: X, y: Y, types: &Types) -> bool
where
Self: 'a,
X: IntoIterator<Item = &'a Self>,
Y: IntoIterator<Item = &'a Self>,
{
let mut x = x.into_iter();
let mut y = y.into_iter();
loop {
match (x.next(), y.next()) {
(None, None) => return true,
(Some(x), Some(y)) => {
if !x.type_eq(y, types) {
return false;
}
}
(_, _) => return false,
}
}
}
}
#[allow(missing_docs)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum BuildInInfo {
U8,
U16,
U32,
U64,
U128,
Usize,
I8,
I16,
I32,
I64,
I128,
Isize,
F32,
F64,
Bool,
String,
Custom(CustomType),
}
macro_rules! impl_from {
($var:ident, $ty:ty) => {
impl From<$ty> for Type {
fn from(value: $ty) -> Self {
Type::new(TypeVariant::$var(value))
}
}
};
}
impl_from!(Reference, ReferenceInfo);
impl_from!(BuildIn, BuildInInfo);
impl_from!(Enumeration, EnumerationInfo);
impl_from!(Dynamic, DynamicInfo);
impl_from!(ComplexType, ComplexInfo);
impl Type {
#[must_use]
pub fn new(variant: TypeVariant) -> Self {
Self {
variant,
display_name: None,
}
}
}
impl Deref for Type {
type Target = TypeVariant;
fn deref(&self) -> &Self::Target {
&self.variant
}
}
impl DerefMut for Type {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.variant
}
}
impl TypeEq for Type {
fn type_hash<H: Hasher>(&self, hasher: &mut H, types: &Types) {
#[allow(clippy::enum_glob_use)]
use TypeVariant::*;
self.display_name.hash(hasher);
match &self.variant {
Union(x) => x.type_hash(hasher, types),
BuildIn(x) => x.hash(hasher),
Reference(x) => x.type_hash(hasher, types),
Enumeration(x) => x.type_hash(hasher, types),
Dynamic(x) => x.type_hash(hasher, types),
All(x) => x.type_hash(hasher, types),
Choice(x) => x.type_hash(hasher, types),
Sequence(x) => x.type_hash(hasher, types),
ComplexType(x) => x.type_hash(hasher, types),
}
}
fn type_eq(&self, other: &Self, types: &Types) -> bool {
#[allow(clippy::enum_glob_use)]
use TypeVariant::*;
if self.display_name != other.display_name {
return false;
}
match (&self.variant, &other.variant) {
(Union(x), Union(y)) => x.type_eq(y, types),
(BuildIn(x), BuildIn(y)) => x == y,
(Reference(x), Reference(y)) => x.type_eq(y, types),
(Enumeration(x), Enumeration(y)) => x.type_eq(y, types),
(Dynamic(x), Dynamic(y)) => x.type_eq(y, types),
(All(x), All(y)) => x.type_eq(y, types),
(Choice(x), Choice(y)) => x.type_eq(y, types),
(Sequence(x), Sequence(y)) => x.type_eq(y, types),
(ComplexType(x), ComplexType(y)) => x.type_eq(y, types),
(_, _) => false,
}
}
}
impl BuildInInfo {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::U8 => "u8",
Self::U16 => "u16",
Self::U32 => "u32",
Self::U64 => "u64",
Self::U128 => "u128",
Self::Usize => "usize",
Self::I8 => "i8",
Self::I16 => "i16",
Self::I32 => "i32",
Self::I64 => "i64",
Self::I128 => "i128",
Self::Isize => "isize",
Self::F32 => "f32",
Self::F64 => "f64",
Self::Bool => "bool",
Self::String => "String",
Self::Custom(x) => x.name(),
}
}
}
impl Display for BuildInInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{}", self.as_str())
}
}
impl<T> TypeEq for Option<T>
where
T: TypeEq,
{
fn type_hash<H: Hasher>(&self, hasher: &mut H, types: &Types) {
if let Some(inner) = self {
hasher.write_u8(1);
inner.type_hash(hasher, types);
} else {
hasher.write_u8(0);
}
}
fn type_eq(&self, other: &Self, types: &Types) -> bool {
match (self, other) {
(Some(x), Some(y)) => x.type_eq(y, types),
(None, None) => true,
(_, _) => false,
}
}
}