use crate::cowstr::CowStr;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ESExprTag<'a> {
Constructor(CowStr<'a>),
Bool,
Int,
Str,
Float16,
Float32,
Float64,
Array8,
Array16,
Array32,
Array64,
Array128,
Null,
}
impl<'a> ESExprTag<'a> {
#[must_use]
pub fn is_constructor(&self, s: &str) -> bool {
match self {
ESExprTag::Constructor(name) => name == s,
_ => false,
}
}
#[must_use]
pub fn into_owned(self) -> ESExprTag<'static> {
match self {
ESExprTag::Constructor(name) => ESExprTag::Constructor(name.into_owned_cowstr()),
ESExprTag::Bool => ESExprTag::Bool,
ESExprTag::Int => ESExprTag::Int,
ESExprTag::Str => ESExprTag::Str,
ESExprTag::Float16 => ESExprTag::Float16,
ESExprTag::Float32 => ESExprTag::Float32,
ESExprTag::Float64 => ESExprTag::Float64,
ESExprTag::Array8 => ESExprTag::Array8,
ESExprTag::Array16 => ESExprTag::Array16,
ESExprTag::Array32 => ESExprTag::Array32,
ESExprTag::Array64 => ESExprTag::Array64,
ESExprTag::Array128 => ESExprTag::Array128,
ESExprTag::Null => ESExprTag::Null,
}
}
#[must_use]
pub fn as_owned(&self) -> ESExprTag<'static> {
match self {
ESExprTag::Constructor(name) => ESExprTag::Constructor(name.as_owned_cowstr()),
ESExprTag::Bool => ESExprTag::Bool,
ESExprTag::Int => ESExprTag::Int,
ESExprTag::Str => ESExprTag::Str,
ESExprTag::Float16 => ESExprTag::Float16,
ESExprTag::Float32 => ESExprTag::Float32,
ESExprTag::Float64 => ESExprTag::Float64,
ESExprTag::Array8 => ESExprTag::Array8,
ESExprTag::Array16 => ESExprTag::Array16,
ESExprTag::Array32 => ESExprTag::Array32,
ESExprTag::Array64 => ESExprTag::Array64,
ESExprTag::Array128 => ESExprTag::Array128,
ESExprTag::Null => ESExprTag::Null,
}
}
#[must_use]
pub fn as_borrowed<'b>(&'b self) -> ESExprTag<'b>
where
'a: 'b,
{
match self {
ESExprTag::Constructor(name) => ESExprTag::Constructor(name.as_borrowed()),
ESExprTag::Bool => ESExprTag::Bool,
ESExprTag::Int => ESExprTag::Int,
ESExprTag::Str => ESExprTag::Str,
ESExprTag::Float16 => ESExprTag::Float16,
ESExprTag::Float32 => ESExprTag::Float32,
ESExprTag::Float64 => ESExprTag::Float64,
ESExprTag::Array8 => ESExprTag::Array8,
ESExprTag::Array16 => ESExprTag::Array16,
ESExprTag::Array32 => ESExprTag::Array32,
ESExprTag::Array64 => ESExprTag::Array64,
ESExprTag::Array128 => ESExprTag::Array128,
ESExprTag::Null => ESExprTag::Null,
}
}
const fn is_equal(&self, b: &ESExprTag) -> bool {
match self {
ESExprTag::Constructor(CowStr::Borrowed(c1) | CowStr::Static(c1)) => match b {
ESExprTag::Constructor(CowStr::Borrowed(c2) | CowStr::Static(c2)) => {
compare_str_bytes(c1.as_bytes(), c2.as_bytes())
},
ESExprTag::Constructor(CowStr::Owned(c2)) => compare_str_bytes(c1.as_bytes(), c2.as_bytes()),
_ => false,
},
ESExprTag::Constructor(CowStr::Owned(c1)) => match b {
ESExprTag::Constructor(CowStr::Borrowed(c2) | CowStr::Static(c2)) => {
compare_str_bytes(c1.as_bytes(), c2.as_bytes())
},
ESExprTag::Constructor(CowStr::Owned(c2)) => compare_str_bytes(c1.as_bytes(), c2.as_bytes()),
_ => false,
},
ESExprTag::Bool => matches!(b, ESExprTag::Bool),
ESExprTag::Int => matches!(b, ESExprTag::Int),
ESExprTag::Str => matches!(b, ESExprTag::Str),
ESExprTag::Float16 => matches!(b, ESExprTag::Float16),
ESExprTag::Float32 => matches!(b, ESExprTag::Float32),
ESExprTag::Float64 => matches!(b, ESExprTag::Float64),
ESExprTag::Array8 => matches!(b, ESExprTag::Array8),
ESExprTag::Array16 => matches!(b, ESExprTag::Array16),
ESExprTag::Array32 => matches!(b, ESExprTag::Array32),
ESExprTag::Array64 => matches!(b, ESExprTag::Array64),
ESExprTag::Array128 => matches!(b, ESExprTag::Array128),
ESExprTag::Null => matches!(b, ESExprTag::Null),
}
}
}
const fn compare_str_bytes(s1: &[u8], s2: &[u8]) -> bool {
if s1.len() != s2.len() {
return false;
}
let mut i = 0;
while i < s1.len() {
if s1[i] != s2[i] {
return false;
}
i += 1;
}
true
}
#[derive(Clone, Copy, Debug)]
pub enum ESExprTagSet {
All,
Tags(&'static [ESExprTag<'static>]),
Concat(&'static [ESExprTagSet]),
}
impl ESExprTagSet {
#[must_use]
pub const fn is_empty(self) -> bool {
match self {
ESExprTagSet::All => false,
ESExprTagSet::Tags(tags) => tags.is_empty(),
ESExprTagSet::Concat(mut collections) => loop {
let Some((&head, tail)) = collections.split_first()
else {
return true;
};
if !head.is_empty() {
return false;
}
collections = tail;
},
}
}
#[must_use]
pub const fn is_all(self) -> bool {
match self {
ESExprTagSet::All => true,
ESExprTagSet::Tags(_) => false,
ESExprTagSet::Concat(mut collections) => loop {
let Some((&head, tail)) = collections.split_first()
else {
return false;
};
if head.is_all() {
return true;
}
collections = tail;
},
}
}
#[must_use]
pub const fn contains(self, tag: &ESExprTag) -> bool {
match self {
ESExprTagSet::All => true,
ESExprTagSet::Tags(mut tags) => loop {
let Some((head, tail)) = tags.split_first()
else {
return false;
};
if head.is_equal(tag) {
return true;
}
tags = tail;
},
ESExprTagSet::Concat(mut collections) => loop {
let Some((&head, tail)) = collections.split_first()
else {
return false;
};
if head.contains(tag) {
return true;
}
collections = tail;
},
}
}
#[must_use]
pub const fn is_disjoint(self, other: ESExprTagSet) -> bool {
match other {
ESExprTagSet::All => self.is_empty(),
ESExprTagSet::Tags(mut tags) => loop {
let Some((head, tail)) = tags.split_first()
else {
return true;
};
if self.contains(head) {
return false;
}
tags = tail;
},
ESExprTagSet::Concat(mut collections) => loop {
let Some((&head, tail)) = collections.split_first()
else {
return true;
};
if !self.is_disjoint(head) {
return false;
}
collections = tail;
},
}
}
#[must_use]
pub const fn is_subset(self, other: ESExprTagSet) -> bool {
match self {
ESExprTagSet::All => other.is_all(),
ESExprTagSet::Tags(mut tags) => loop {
let Some((head, tail)) = tags.split_first()
else {
return true;
};
if !other.contains(head) {
return false;
}
tags = tail;
},
ESExprTagSet::Concat(mut collections) => loop {
let Some((&head, tail)) = collections.split_first()
else {
return true;
};
if !head.is_subset(other) {
return false;
}
collections = tail;
},
}
}
#[must_use]
pub const fn is_equal(self, other: ESExprTagSet) -> bool {
self.is_subset(other) && other.is_subset(self)
}
}
impl PartialEq for ESExprTagSet {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.is_equal(*other)
}
}
impl Eq for ESExprTagSet {}
#[cfg(test)]
mod tests {
use super::*;
use crate::ESExprCodec;
#[test]
fn tag_collection_disjoint() {
assert!(
ESExprTagSet::Tags(&[ESExprTag::Int]).is_disjoint(ESExprTagSet::Tags(&[ESExprTag::Float32])),
);
assert!(
ESExprTagSet::Concat(&[
i32::TAGS,
f32::TAGS,
],)
.is_disjoint(<alloc::string::String as ESExprCodec>::TAGS)
);
}
#[test]
fn tag_collection_disjoint_substr() {
let t1: ESExprTagSet = ESExprTagSet::Tags(
&[
ESExprTag::Constructor(
CowStr::Borrowed("record"),
),
],
);
let t2: ESExprTagSet = ESExprTagSet::Tags(
&[
ESExprTag::Constructor(
CowStr::Borrowed("record-field-literal"),
),
],
);
assert!(t1.is_disjoint(t2));
}
}