#[doc(inline)]
pub use typst_macros::{scope, ty};
use std::cmp::Ordering;
use std::fmt::{self, Debug, Display, Formatter};
use std::sync::LazyLock;
use ecow::{EcoString, eco_format};
use typst_utils::Static;
use crate::diag::{DeprecationSink, StrResult, bail};
use crate::foundations::{
AutoValue, Func, NativeFuncData, NoneValue, Repr, Scope, Value, cast, func,
};
#[ty(scope, cast)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Type(Static<NativeTypeData>);
impl Type {
pub fn of<T: NativeType>() -> Self {
T::ty()
}
pub fn short_name(&self) -> &'static str {
self.0.name
}
pub fn long_name(&self) -> &'static str {
self.0.long_name
}
pub fn title(&self) -> &'static str {
self.0.title
}
pub fn docs(&self) -> &'static str {
self.0.docs
}
pub fn keywords(&self) -> &'static [&'static str] {
self.0.keywords
}
pub fn constructor(&self) -> StrResult<Func> {
self.0
.constructor
.as_ref()
.map(|lazy| Func::from(*lazy))
.ok_or_else(|| eco_format!("type {self} does not have a constructor"))
}
pub fn scope(&self) -> &'static Scope {
&(self.0).0.scope
}
pub fn field(
&self,
field: &str,
sink: impl DeprecationSink,
) -> StrResult<&'static Value> {
match self.scope().get(field) {
Some(binding) => Ok(binding.read_checked(sink)),
None => bail!("type {self} does not contain field `{field}`"),
}
}
}
#[scope]
impl Type {
#[func(constructor)]
pub fn construct(
value: Value,
) -> Type {
value.ty()
}
}
impl Debug for Type {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Type({})", self.long_name())
}
}
impl Repr for Type {
fn repr(&self) -> EcoString {
if *self == Type::of::<AutoValue>() {
"type(auto)"
} else if *self == Type::of::<NoneValue>() {
"type(none)"
} else {
self.short_name()
}
.into()
}
}
impl Display for Type {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(self.long_name())
}
}
impl Ord for Type {
fn cmp(&self, other: &Self) -> Ordering {
self.long_name().cmp(other.long_name())
}
}
impl PartialOrd for Type {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
pub trait NativeType {
const NAME: &'static str;
fn ty() -> Type {
Type::from(Self::data())
}
fn data() -> &'static NativeTypeData;
}
#[derive(Debug)]
pub struct NativeTypeData {
pub name: &'static str,
pub long_name: &'static str,
pub title: &'static str,
pub docs: &'static str,
pub keywords: &'static [&'static str],
pub constructor: LazyLock<Option<&'static NativeFuncData>>,
pub scope: LazyLock<Scope>,
}
impl From<&'static NativeTypeData> for Type {
fn from(data: &'static NativeTypeData) -> Self {
Self(Static(data))
}
}
cast! {
&'static NativeTypeData,
self => Type::from(self).into_value(),
}