use std::borrow::Cow;
use std::fmt::Write;
use std::hash::Hash;
use std::ops::Add;
use ecow::{eco_format, EcoString};
use smallvec::SmallVec;
use unicode_math_class::MathClass;
use crate::diag::{At, HintedStrResult, SourceResult, StrResult};
use crate::foundations::{array, repr, NativeElement, Packed, Repr, Str, Type, Value};
use crate::syntax::{Span, Spanned};
#[rustfmt::skip]
#[doc(inline)]
pub use typst_macros::{cast, Cast};
pub trait Reflect {
fn input() -> CastInfo;
fn output() -> CastInfo;
fn castable(value: &Value) -> bool;
fn error(found: &Value) -> EcoString {
Self::input().error(found)
}
}
impl Reflect for Value {
fn input() -> CastInfo {
CastInfo::Any
}
fn output() -> CastInfo {
CastInfo::Any
}
fn castable(_: &Value) -> bool {
true
}
}
impl<T: Reflect> Reflect for Spanned<T> {
fn input() -> CastInfo {
T::input()
}
fn output() -> CastInfo {
T::output()
}
fn castable(value: &Value) -> bool {
T::castable(value)
}
}
impl<T: NativeElement + Reflect> Reflect for Packed<T> {
fn input() -> CastInfo {
T::input()
}
fn output() -> CastInfo {
T::output()
}
fn castable(value: &Value) -> bool {
T::castable(value)
}
}
impl<T: Reflect> Reflect for StrResult<T> {
fn input() -> CastInfo {
T::input()
}
fn output() -> CastInfo {
T::output()
}
fn castable(value: &Value) -> bool {
T::castable(value)
}
}
impl<T: Reflect> Reflect for HintedStrResult<T> {
fn input() -> CastInfo {
T::input()
}
fn output() -> CastInfo {
T::output()
}
fn castable(value: &Value) -> bool {
T::castable(value)
}
}
impl<T: Reflect> Reflect for SourceResult<T> {
fn input() -> CastInfo {
T::input()
}
fn output() -> CastInfo {
T::output()
}
fn castable(value: &Value) -> bool {
T::castable(value)
}
}
impl<T: Reflect> Reflect for &T {
fn input() -> CastInfo {
T::input()
}
fn output() -> CastInfo {
T::output()
}
fn castable(value: &Value) -> bool {
T::castable(value)
}
}
impl<T: Reflect> Reflect for &mut T {
fn input() -> CastInfo {
T::input()
}
fn output() -> CastInfo {
T::output()
}
fn castable(value: &Value) -> bool {
T::castable(value)
}
}
pub trait IntoValue {
fn into_value(self) -> Value;
}
impl IntoValue for Value {
fn into_value(self) -> Value {
self
}
}
impl IntoValue for (&Str, &Value) {
fn into_value(self) -> Value {
Value::Array(array![self.0.clone(), self.1.clone()])
}
}
impl<T: IntoValue + Clone> IntoValue for Cow<'_, T> {
fn into_value(self) -> Value {
self.into_owned().into_value()
}
}
impl<T: NativeElement + IntoValue> IntoValue for Packed<T> {
fn into_value(self) -> Value {
Value::Content(self.pack())
}
}
impl<T: IntoValue> IntoValue for Spanned<T> {
fn into_value(self) -> Value {
self.v.into_value()
}
}
pub trait IntoResult {
fn into_result(self, span: Span) -> SourceResult<Value>;
}
impl<T: IntoValue> IntoResult for T {
fn into_result(self, _: Span) -> SourceResult<Value> {
Ok(self.into_value())
}
}
impl<T: IntoValue> IntoResult for StrResult<T> {
fn into_result(self, span: Span) -> SourceResult<Value> {
self.map(IntoValue::into_value).at(span)
}
}
impl<T: IntoValue> IntoResult for HintedStrResult<T> {
fn into_result(self, span: Span) -> SourceResult<Value> {
self.map(IntoValue::into_value).at(span)
}
}
impl<T: IntoValue> IntoResult for SourceResult<T> {
fn into_result(self, _: Span) -> SourceResult<Value> {
self.map(IntoValue::into_value)
}
}
impl<T: IntoValue> IntoValue for fn() -> T {
fn into_value(self) -> Value {
self().into_value()
}
}
pub trait FromValue<V = Value>: Sized + Reflect {
fn from_value(value: V) -> StrResult<Self>;
}
impl FromValue for Value {
fn from_value(value: Value) -> StrResult<Self> {
Ok(value)
}
}
impl<T: NativeElement + FromValue> FromValue for Packed<T> {
fn from_value(mut value: Value) -> StrResult<Self> {
if let Value::Content(content) = value {
match content.into_packed::<T>() {
Ok(packed) => return Ok(packed),
Err(content) => value = Value::Content(content),
}
}
let val = T::from_value(value)?;
Ok(Packed::new(val))
}
}
impl<T: FromValue> FromValue<Spanned<Value>> for T {
fn from_value(value: Spanned<Value>) -> StrResult<Self> {
T::from_value(value.v)
}
}
impl<T: FromValue> FromValue<Spanned<Value>> for Spanned<T> {
fn from_value(value: Spanned<Value>) -> StrResult<Self> {
let span = value.span;
T::from_value(value.v).map(|t| Spanned::new(t, span))
}
}
#[derive(Debug, Clone, PartialEq, Hash, PartialOrd)]
pub enum CastInfo {
Any,
Value(Value, &'static str),
Type(Type),
Union(Vec<Self>),
}
impl CastInfo {
pub fn error(&self, found: &Value) -> EcoString {
let mut matching_type = false;
let mut parts = vec![];
self.walk(|info| match info {
CastInfo::Any => parts.push("anything".into()),
CastInfo::Value(value, _) => {
parts.push(value.repr());
if value.ty() == found.ty() {
matching_type = true;
}
}
CastInfo::Type(ty) => parts.push(eco_format!("{ty}")),
CastInfo::Union(_) => {}
});
let mut msg = String::from("expected ");
if parts.is_empty() {
msg.push_str(" nothing");
}
msg.push_str(&repr::separated_list(&parts, "or"));
if !matching_type {
msg.push_str(", found ");
write!(msg, "{}", found.ty()).unwrap();
}
if let Value::Int(i) = found {
if parts.iter().any(|p| p == "length") && !matching_type {
write!(msg, ": a length needs a unit - did you mean {i}pt?").unwrap();
}
}
msg.into()
}
pub fn walk<F>(&self, mut f: F)
where
F: FnMut(&Self),
{
fn inner<F>(info: &CastInfo, f: &mut F)
where
F: FnMut(&CastInfo),
{
if let CastInfo::Union(infos) = info {
for child in infos {
inner(child, f);
}
} else {
f(info);
}
}
inner(self, &mut f)
}
}
impl Add for CastInfo {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self::Union(match (self, rhs) {
(Self::Union(mut lhs), Self::Union(rhs)) => {
for cast in rhs {
if !lhs.contains(&cast) {
lhs.push(cast);
}
}
lhs
}
(Self::Union(mut lhs), rhs) => {
if !lhs.contains(&rhs) {
lhs.push(rhs);
}
lhs
}
(lhs, Self::Union(mut rhs)) => {
if !rhs.contains(&lhs) {
rhs.insert(0, lhs);
}
rhs
}
(lhs, rhs) => vec![lhs, rhs],
})
}
}
pub trait Container {
type Inner;
}
impl<T> Container for Option<T> {
type Inner = T;
}
impl<T> Container for Vec<T> {
type Inner = T;
}
impl<T, const N: usize> Container for SmallVec<[T; N]> {
type Inner = T;
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Never {}
impl Reflect for Never {
fn input() -> CastInfo {
CastInfo::Union(vec![])
}
fn output() -> CastInfo {
CastInfo::Union(vec![])
}
fn castable(_: &Value) -> bool {
false
}
}
impl IntoValue for Never {
fn into_value(self) -> Value {
match self {}
}
}
impl FromValue for Never {
fn from_value(value: Value) -> StrResult<Self> {
Err(Self::error(&value))
}
}
cast! {
MathClass,
self => IntoValue::into_value(match self {
MathClass::Normal => "normal",
MathClass::Alphabetic => "alphabetic",
MathClass::Binary => "binary",
MathClass::Closing => "closing",
MathClass::Diacritic => "diacritic",
MathClass::Fence => "fence",
MathClass::GlyphPart => "glyph-part",
MathClass::Large => "large",
MathClass::Opening => "opening",
MathClass::Punctuation => "punctuation",
MathClass::Relation => "relation",
MathClass::Space => "space",
MathClass::Unary => "unary",
MathClass::Vary => "vary",
MathClass::Special => "special",
}),
"normal" => MathClass::Normal,
"punctuation" => MathClass::Punctuation,
"opening" => MathClass::Opening,
"closing" => MathClass::Closing,
"fence" => MathClass::Fence,
"large" => MathClass::Large,
"relation" => MathClass::Relation,
"unary" => MathClass::Unary,
"binary" => MathClass::Binary,
"vary" => MathClass::Vary,
}