use std::sync::Arc;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use crate::Union;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FnParam {
pub name: Arc<str>,
pub ty: Option<Union>,
pub default: Option<Union>,
pub is_variadic: bool,
pub is_byref: bool,
pub is_optional: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
pub enum Variance {
#[default]
Invariant,
Covariant,
Contravariant,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TemplateParam {
pub name: Arc<str>,
pub bound: Option<Union>,
pub variance: Variance,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ArrayKey {
String(Arc<str>),
Int(i64),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KeyedProperty {
pub ty: Union,
pub optional: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Atomic {
TString,
TLiteralString(Arc<str>),
TClassString(Option<Arc<str>>),
TNonEmptyString,
TNumericString,
TInt,
TLiteralInt(i64),
TIntRange { min: Option<i64>, max: Option<i64> },
TPositiveInt,
TNegativeInt,
TNonNegativeInt,
TFloat,
TLiteralFloat(i64, i64), TBool,
TTrue,
TFalse,
TNull,
TVoid,
TNever,
TMixed,
TScalar,
TNumeric,
TObject,
TNamedObject {
fqcn: Arc<str>,
type_params: Vec<Union>,
},
TStaticObject { fqcn: Arc<str> },
TSelf { fqcn: Arc<str> },
TParent { fqcn: Arc<str> },
TCallable {
params: Option<Vec<FnParam>>,
return_type: Option<Box<Union>>,
},
TClosure {
params: Vec<FnParam>,
return_type: Box<Union>,
this_type: Option<Box<Union>>,
},
TArray { key: Box<Union>, value: Box<Union> },
TList { value: Box<Union> },
TNonEmptyArray { key: Box<Union>, value: Box<Union> },
TNonEmptyList { value: Box<Union> },
TKeyedArray {
properties: IndexMap<ArrayKey, KeyedProperty>,
is_open: bool,
is_list: bool,
},
TTemplateParam {
name: Arc<str>,
as_type: Box<Union>,
defining_entity: Arc<str>,
},
TConditional {
subject: Box<Union>,
if_true: Box<Union>,
if_false: Box<Union>,
},
TInterfaceString,
TEnumString,
TTraitString,
}
impl Atomic {
pub fn can_be_falsy(&self) -> bool {
match self {
Atomic::TNull
| Atomic::TFalse
| Atomic::TBool
| Atomic::TNever
| Atomic::TLiteralInt(0)
| Atomic::TLiteralFloat(0, 0)
| Atomic::TInt
| Atomic::TFloat
| Atomic::TNumeric
| Atomic::TScalar
| Atomic::TMixed
| Atomic::TString
| Atomic::TNonEmptyString
| Atomic::TArray { .. }
| Atomic::TList { .. }
| Atomic::TNonEmptyArray { .. }
| Atomic::TNonEmptyList { .. } => true,
Atomic::TLiteralString(s) => s.as_ref().is_empty() || s.as_ref() == "0",
Atomic::TKeyedArray { properties, .. } => properties.is_empty(),
_ => false,
}
}
pub fn can_be_truthy(&self) -> bool {
match self {
Atomic::TNever | Atomic::TVoid => false,
Atomic::TNull | Atomic::TFalse => false,
Atomic::TLiteralInt(0) => false,
Atomic::TLiteralFloat(0, 0) => false,
Atomic::TLiteralString(s) if s.as_ref() == "" || s.as_ref() == "0" => false,
_ => true,
}
}
pub fn is_numeric(&self) -> bool {
matches!(
self,
Atomic::TInt
| Atomic::TLiteralInt(_)
| Atomic::TIntRange { .. }
| Atomic::TPositiveInt
| Atomic::TNegativeInt
| Atomic::TNonNegativeInt
| Atomic::TFloat
| Atomic::TLiteralFloat(..)
| Atomic::TNumeric
| Atomic::TNumericString
)
}
pub fn is_int(&self) -> bool {
matches!(
self,
Atomic::TInt
| Atomic::TLiteralInt(_)
| Atomic::TIntRange { .. }
| Atomic::TPositiveInt
| Atomic::TNegativeInt
| Atomic::TNonNegativeInt
)
}
pub fn is_string(&self) -> bool {
matches!(
self,
Atomic::TString
| Atomic::TLiteralString(_)
| Atomic::TClassString(_)
| Atomic::TNonEmptyString
| Atomic::TNumericString
| Atomic::TInterfaceString
| Atomic::TEnumString
| Atomic::TTraitString
)
}
pub fn is_array(&self) -> bool {
matches!(
self,
Atomic::TArray { .. }
| Atomic::TList { .. }
| Atomic::TNonEmptyArray { .. }
| Atomic::TNonEmptyList { .. }
| Atomic::TKeyedArray { .. }
)
}
pub fn is_object(&self) -> bool {
matches!(
self,
Atomic::TObject
| Atomic::TNamedObject { .. }
| Atomic::TStaticObject { .. }
| Atomic::TSelf { .. }
| Atomic::TParent { .. }
)
}
pub fn is_callable(&self) -> bool {
matches!(self, Atomic::TCallable { .. } | Atomic::TClosure { .. })
}
pub fn named_object_fqcn(&self) -> Option<&str> {
match self {
Atomic::TNamedObject { fqcn, .. }
| Atomic::TStaticObject { fqcn }
| Atomic::TSelf { fqcn }
| Atomic::TParent { fqcn } => Some(fqcn.as_ref()),
_ => None,
}
}
pub fn type_name(&self) -> &'static str {
match self {
Atomic::TString
| Atomic::TLiteralString(_)
| Atomic::TNonEmptyString
| Atomic::TNumericString => "string",
Atomic::TClassString(_) => "class-string",
Atomic::TInt | Atomic::TLiteralInt(_) | Atomic::TIntRange { .. } => "int",
Atomic::TPositiveInt => "positive-int",
Atomic::TNegativeInt => "negative-int",
Atomic::TNonNegativeInt => "non-negative-int",
Atomic::TFloat | Atomic::TLiteralFloat(..) => "float",
Atomic::TBool => "bool",
Atomic::TTrue => "true",
Atomic::TFalse => "false",
Atomic::TNull => "null",
Atomic::TVoid => "void",
Atomic::TNever => "never",
Atomic::TMixed => "mixed",
Atomic::TScalar => "scalar",
Atomic::TNumeric => "numeric",
Atomic::TObject => "object",
Atomic::TNamedObject { .. } => "object",
Atomic::TStaticObject { .. } => "static",
Atomic::TSelf { .. } => "self",
Atomic::TParent { .. } => "parent",
Atomic::TCallable { .. } => "callable",
Atomic::TClosure { .. } => "Closure",
Atomic::TArray { .. } => "array",
Atomic::TList { .. } => "list",
Atomic::TNonEmptyArray { .. } => "non-empty-array",
Atomic::TNonEmptyList { .. } => "non-empty-list",
Atomic::TKeyedArray { .. } => "array",
Atomic::TTemplateParam { .. } => "template-param",
Atomic::TConditional { .. } => "conditional-type",
Atomic::TInterfaceString => "interface-string",
Atomic::TEnumString => "enum-string",
Atomic::TTraitString => "trait-string",
}
}
}