use std::fmt;
use crate::value::Value;
#[derive(Debug, Clone, PartialEq)]
pub enum Type {
Null,
Number,
String,
Boolean,
Color,
Object,
Value,
Array(Box<Type>, Option<usize>),
ProjectionDefinition,
Collator,
Formatted,
Padding,
NumberArray,
ColorArray,
ResolvedImage,
VariableAnchorOffsetCollection,
}
impl Type {
pub fn array(item: Type, n: Option<usize>) -> Type {
Type::Array(Box::new(item), n)
}
pub fn kind(&self) -> &'static str {
match self {
Type::Null => "null",
Type::Number => "number",
Type::String => "string",
Type::Boolean => "boolean",
Type::Color => "color",
Type::Object => "object",
Type::Value => "value",
Type::Array(..) => "array",
Type::ProjectionDefinition => "projectionDefinition",
Type::Collator => "collator",
Type::Formatted => "formatted",
Type::Padding => "padding",
Type::NumberArray => "numberArray",
Type::ColorArray => "colorArray",
Type::ResolvedImage => "resolvedImage",
Type::VariableAnchorOffsetCollection => "variableAnchorOffsetCollection",
}
}
pub fn of_value(v: &Value) -> Type {
match v {
Value::Null => Type::Null,
Value::Bool(_) => Type::Boolean,
Value::Number(_) => Type::Number,
Value::String(_) => Type::String,
Value::Color(_) => Type::Color,
Value::Object(_) => Type::Object,
Value::Image { .. } => Type::ResolvedImage,
Value::Formatted(_) => Type::Formatted,
Value::NumberArray(_) => Type::NumberArray,
Value::ColorArray(_) => Type::ColorArray,
Value::Padding(_) => Type::Padding,
Value::Projection(_) => Type::ProjectionDefinition,
Value::Collator { .. } => Type::Collator,
Value::Array(items) => {
let mut item_type: Option<Type> = None;
for it in items {
let t = Type::of_value(it);
match &item_type {
None => item_type = Some(t),
Some(existing) if *existing == t => {}
Some(_) => {
item_type = Some(Type::Value);
break;
}
}
}
Type::array(item_type.unwrap_or(Type::Value), Some(items.len()))
}
}
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Type::Array(item, Some(n)) => write!(f, "array<{item}, {n}>"),
Type::Array(item, None) => {
if matches!(**item, Type::Value) {
write!(f, "array")
} else {
write!(f, "array<{item}>")
}
}
other => write!(f, "{}", other.kind()),
}
}
}
pub fn is_subtype(expected: &Type, t: &Type) -> bool {
if let Type::Array(exp_item, exp_n) = expected {
if let Type::Array(t_item, t_n) = t {
let item_ok = (*t_n == Some(0) && matches!(**t_item, Type::Value))
|| is_subtype(exp_item, t_item);
let n_ok = exp_n.is_none() || exp_n == t_n;
return item_ok && n_ok;
}
return false;
}
if expected.kind() == t.kind() {
return true;
}
if matches!(expected, Type::Value) {
return is_value_member(t);
}
false
}
fn is_value_member(t: &Type) -> bool {
!matches!(t, Type::Collator)
}