#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Primitive {
Number,
String,
Boolean,
BigInt,
Symbol,
Null,
Undefined,
}
impl Primitive {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Number => "number",
Self::String => "string",
Self::Boolean => "boolean",
Self::BigInt => "bigint",
Self::Symbol => "symbol",
Self::Null => "null",
Self::Undefined => "undefined",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Symbol {
pub name: String,
pub exported: Option<String>,
pub module: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Type {
Primitive(Primitive),
Nominal {
name: String,
symbol: Option<Symbol>,
},
Union(Vec<Type>),
}
impl Type {
#[must_use]
pub fn union(members: Vec<Self>) -> Option<Self> {
let mut flattened: Vec<Self> = Vec::with_capacity(members.len());
for member in members {
match member {
Self::Union(inner) => flattened.extend(inner),
other => flattened.push(other),
}
}
flattened.sort();
flattened.dedup();
match flattened.len() {
0 => None,
1 => flattened.pop(),
_ => Some(Self::Union(flattened)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_union_of_one_is_that_type() {
assert_eq!(
Type::union(vec![Type::Primitive(Primitive::Number)]),
Some(Type::Primitive(Primitive::Number))
);
}
#[test]
fn a_union_of_none_is_nothing() {
assert_eq!(Type::union(Vec::new()), None);
}
#[test]
fn union_members_do_not_depend_on_the_order_they_were_written() {
let one = Type::union(vec![
Type::Primitive(Primitive::String),
Type::Primitive(Primitive::Number),
]);
let other = Type::union(vec![
Type::Primitive(Primitive::Number),
Type::Primitive(Primitive::String),
]);
assert_eq!(one, other);
}
#[test]
fn primitives_sort_before_nominals() {
let Some(Type::Union(members)) = Type::union(vec![
Type::Nominal {
name: "Decimal".to_owned(),
symbol: None,
},
Type::Primitive(Primitive::Number),
]) else {
panic!("two distinct members make a union");
};
assert_eq!(members[0], Type::Primitive(Primitive::Number));
}
#[test]
fn a_repeated_member_appears_once() {
assert_eq!(
Type::union(vec![
Type::Primitive(Primitive::Number),
Type::Primitive(Primitive::Number),
]),
Some(Type::Primitive(Primitive::Number))
);
}
#[test]
fn a_nested_union_flattens_one_level() {
let inner = Type::union(vec![
Type::Primitive(Primitive::Number),
Type::Primitive(Primitive::String),
])
.expect("two members");
let Some(Type::Union(members)) =
Type::union(vec![inner, Type::Primitive(Primitive::Boolean)])
else {
panic!("three distinct members make a union");
};
assert_eq!(members.len(), 3);
}
#[test]
fn the_oracle_identity_is_populated_and_stable() {
let once = crate::oracle_identity();
assert_ne!(once, [0_u8; 32], "the build script did not write a digest");
assert_eq!(once, crate::oracle_identity());
}
}