typst_library/foundations/
cast.rs

1#[rustfmt::skip]
2#[doc(inline)]
3pub use typst_macros::{cast, Cast};
4
5use std::borrow::Cow;
6use std::fmt::Write;
7use std::hash::Hash;
8use std::ops::Add;
9
10use ecow::eco_format;
11use smallvec::SmallVec;
12use typst_syntax::{Span, Spanned};
13use unicode_math_class::MathClass;
14
15use crate::diag::{At, HintedStrResult, HintedString, SourceResult, StrResult};
16use crate::foundations::{
17    array, repr, Fold, NativeElement, Packed, Repr, Str, Type, Value,
18};
19
20/// Determine details of a type.
21///
22/// Type casting works as follows:
23/// - [`Reflect for T`](Reflect) describes the possible Typst values for `T`
24///    (for documentation and autocomplete).
25/// - [`IntoValue for T`](IntoValue) is for conversion from `T -> Value`
26///   (infallible)
27/// - [`FromValue for T`](FromValue) is for conversion from `Value -> T`
28///   (fallible).
29///
30/// We can't use `TryFrom<Value>` due to conflicting impls. We could use
31/// `From<T> for Value`, but that inverses the impl and leads to tons of
32/// `.into()` all over the place that become hard to decipher.
33pub trait Reflect {
34    /// Describe what can be cast into this value.
35    fn input() -> CastInfo;
36
37    /// Describe what this value can be cast into.
38    fn output() -> CastInfo;
39
40    /// Whether the given value can be converted to `T`.
41    ///
42    /// This exists for performance. The check could also be done through the
43    /// [`CastInfo`], but it would be much more expensive (heap allocation +
44    /// dynamic checks instead of optimized machine code for each type).
45    fn castable(value: &Value) -> bool;
46
47    /// Produce an error message for an unacceptable value type.
48    ///
49    /// ```ignore
50    /// assert_eq!(
51    ///   <i64 as Reflect>::error(&Value::None),
52    ///   "expected integer, found none",
53    /// );
54    /// ```
55    fn error(found: &Value) -> HintedString {
56        Self::input().error(found)
57    }
58}
59
60impl Reflect for Value {
61    fn input() -> CastInfo {
62        CastInfo::Any
63    }
64
65    fn output() -> CastInfo {
66        CastInfo::Any
67    }
68
69    fn castable(_: &Value) -> bool {
70        true
71    }
72}
73
74impl<T: Reflect> Reflect for Spanned<T> {
75    fn input() -> CastInfo {
76        T::input()
77    }
78
79    fn output() -> CastInfo {
80        T::output()
81    }
82
83    fn castable(value: &Value) -> bool {
84        T::castable(value)
85    }
86}
87
88impl<T: NativeElement + Reflect> Reflect for Packed<T> {
89    fn input() -> CastInfo {
90        T::input()
91    }
92
93    fn output() -> CastInfo {
94        T::output()
95    }
96
97    fn castable(value: &Value) -> bool {
98        T::castable(value)
99    }
100}
101
102impl<T: Reflect> Reflect for StrResult<T> {
103    fn input() -> CastInfo {
104        T::input()
105    }
106
107    fn output() -> CastInfo {
108        T::output()
109    }
110
111    fn castable(value: &Value) -> bool {
112        T::castable(value)
113    }
114}
115
116impl<T: Reflect> Reflect for HintedStrResult<T> {
117    fn input() -> CastInfo {
118        T::input()
119    }
120
121    fn output() -> CastInfo {
122        T::output()
123    }
124
125    fn castable(value: &Value) -> bool {
126        T::castable(value)
127    }
128}
129
130impl<T: Reflect> Reflect for SourceResult<T> {
131    fn input() -> CastInfo {
132        T::input()
133    }
134
135    fn output() -> CastInfo {
136        T::output()
137    }
138
139    fn castable(value: &Value) -> bool {
140        T::castable(value)
141    }
142}
143
144impl<T: Reflect> Reflect for &T {
145    fn input() -> CastInfo {
146        T::input()
147    }
148
149    fn output() -> CastInfo {
150        T::output()
151    }
152
153    fn castable(value: &Value) -> bool {
154        T::castable(value)
155    }
156}
157
158impl<T: Reflect> Reflect for &mut T {
159    fn input() -> CastInfo {
160        T::input()
161    }
162
163    fn output() -> CastInfo {
164        T::output()
165    }
166
167    fn castable(value: &Value) -> bool {
168        T::castable(value)
169    }
170}
171
172/// Cast a Rust type into a Typst [`Value`].
173///
174/// See also: [`Reflect`].
175pub trait IntoValue {
176    /// Cast this type into a value.
177    fn into_value(self) -> Value;
178}
179
180impl IntoValue for Value {
181    fn into_value(self) -> Value {
182        self
183    }
184}
185
186impl IntoValue for (&Str, &Value) {
187    fn into_value(self) -> Value {
188        Value::Array(array![self.0.clone(), self.1.clone()])
189    }
190}
191
192impl<T: IntoValue + Clone> IntoValue for Cow<'_, T> {
193    fn into_value(self) -> Value {
194        self.into_owned().into_value()
195    }
196}
197
198impl<T: NativeElement + IntoValue> IntoValue for Packed<T> {
199    fn into_value(self) -> Value {
200        Value::Content(self.pack())
201    }
202}
203
204impl<T: IntoValue> IntoValue for Spanned<T> {
205    fn into_value(self) -> Value {
206        self.v.into_value()
207    }
208}
209
210/// Cast a Rust type or result into a [`SourceResult<Value>`].
211///
212/// Converts `T`, [`StrResult<T>`], or [`SourceResult<T>`] into
213/// [`SourceResult<Value>`] by `Ok`-wrapping or adding span information.
214pub trait IntoResult {
215    /// Cast this type into a value.
216    fn into_result(self, span: Span) -> SourceResult<Value>;
217}
218
219impl<T: IntoValue> IntoResult for T {
220    fn into_result(self, _: Span) -> SourceResult<Value> {
221        Ok(self.into_value())
222    }
223}
224
225impl<T: IntoValue> IntoResult for StrResult<T> {
226    fn into_result(self, span: Span) -> SourceResult<Value> {
227        self.map(IntoValue::into_value).at(span)
228    }
229}
230
231impl<T: IntoValue> IntoResult for HintedStrResult<T> {
232    fn into_result(self, span: Span) -> SourceResult<Value> {
233        self.map(IntoValue::into_value).at(span)
234    }
235}
236
237impl<T: IntoValue> IntoResult for SourceResult<T> {
238    fn into_result(self, _: Span) -> SourceResult<Value> {
239        self.map(IntoValue::into_value)
240    }
241}
242
243impl<T: IntoValue> IntoValue for fn() -> T {
244    fn into_value(self) -> Value {
245        self().into_value()
246    }
247}
248
249/// Try to cast a Typst [`Value`] into a Rust type.
250///
251/// See also: [`Reflect`].
252pub trait FromValue<V = Value>: Sized + Reflect {
253    /// Try to cast the value into an instance of `Self`.
254    fn from_value(value: V) -> HintedStrResult<Self>;
255}
256
257impl FromValue for Value {
258    fn from_value(value: Value) -> HintedStrResult<Self> {
259        Ok(value)
260    }
261}
262
263impl<T: NativeElement + FromValue> FromValue for Packed<T> {
264    fn from_value(mut value: Value) -> HintedStrResult<Self> {
265        if let Value::Content(content) = value {
266            match content.into_packed::<T>() {
267                Ok(packed) => return Ok(packed),
268                Err(content) => value = Value::Content(content),
269            }
270        }
271        let val = T::from_value(value)?;
272        Ok(Packed::new(val))
273    }
274}
275
276impl<T: FromValue> FromValue<Spanned<Value>> for T {
277    fn from_value(value: Spanned<Value>) -> HintedStrResult<Self> {
278        T::from_value(value.v)
279    }
280}
281
282impl<T: FromValue> FromValue<Spanned<Value>> for Spanned<T> {
283    fn from_value(value: Spanned<Value>) -> HintedStrResult<Self> {
284        let span = value.span;
285        T::from_value(value.v).map(|t| Spanned::new(t, span))
286    }
287}
288
289/// Describes a possible value for a cast.
290#[derive(Debug, Clone, PartialEq, Hash, PartialOrd)]
291pub enum CastInfo {
292    /// Any value is okay.
293    Any,
294    /// A specific value, plus short documentation for that value.
295    Value(Value, &'static str),
296    /// Any value of a type.
297    Type(Type),
298    /// Multiple alternatives.
299    Union(Vec<Self>),
300}
301
302impl CastInfo {
303    /// Produce an error message describing what was expected and what was
304    /// found.
305    pub fn error(&self, found: &Value) -> HintedString {
306        let mut matching_type = false;
307        let mut parts = vec![];
308
309        self.walk(|info| match info {
310            CastInfo::Any => parts.push("anything".into()),
311            CastInfo::Value(value, _) => {
312                parts.push(value.repr());
313                if value.ty() == found.ty() {
314                    matching_type = true;
315                }
316            }
317            CastInfo::Type(ty) => parts.push(eco_format!("{ty}")),
318            CastInfo::Union(_) => {}
319        });
320
321        let mut msg = String::from("expected ");
322        if parts.is_empty() {
323            msg.push_str(" nothing");
324        }
325
326        msg.push_str(&repr::separated_list(&parts, "or"));
327
328        if !matching_type {
329            msg.push_str(", found ");
330            write!(msg, "{}", found.ty()).unwrap();
331        }
332
333        let mut msg: HintedString = msg.into();
334
335        if let Value::Int(i) = found {
336            if !matching_type && parts.iter().any(|p| p == "length") {
337                msg.hint(eco_format!("a length needs a unit - did you mean {i}pt?"));
338            }
339        } else if let Value::Str(s) = found {
340            if !matching_type && parts.iter().any(|p| p == "label") {
341                if typst_syntax::is_valid_label_literal_id(s) {
342                    msg.hint(eco_format!(
343                        "use `<{s}>` or `label({})` to create a label",
344                        s.repr()
345                    ));
346                } else {
347                    msg.hint(eco_format!("use `label({})` to create a label", s.repr()));
348                }
349            }
350        } else if let Value::Decimal(_) = found {
351            if !matching_type && parts.iter().any(|p| p == "float") {
352                msg.hint(eco_format!(
353                    "if loss of precision is acceptable, explicitly cast the \
354                     decimal to a float with `float(value)`"
355                ));
356            }
357        }
358
359        msg
360    }
361
362    /// Walk all contained non-union infos.
363    pub fn walk<F>(&self, mut f: F)
364    where
365        F: FnMut(&Self),
366    {
367        fn inner<F>(info: &CastInfo, f: &mut F)
368        where
369            F: FnMut(&CastInfo),
370        {
371            if let CastInfo::Union(infos) = info {
372                for child in infos {
373                    inner(child, f);
374                }
375            } else {
376                f(info);
377            }
378        }
379
380        inner(self, &mut f)
381    }
382}
383
384impl Add for CastInfo {
385    type Output = Self;
386
387    fn add(self, rhs: Self) -> Self {
388        Self::Union(match (self, rhs) {
389            (Self::Union(mut lhs), Self::Union(rhs)) => {
390                for cast in rhs {
391                    if !lhs.contains(&cast) {
392                        lhs.push(cast);
393                    }
394                }
395                lhs
396            }
397            (Self::Union(mut lhs), rhs) => {
398                if !lhs.contains(&rhs) {
399                    lhs.push(rhs);
400                }
401                lhs
402            }
403            (lhs, Self::Union(mut rhs)) => {
404                if !rhs.contains(&lhs) {
405                    rhs.insert(0, lhs);
406                }
407                rhs
408            }
409            (lhs, rhs) => vec![lhs, rhs],
410        })
411    }
412}
413
414/// A container for an argument.
415pub trait Container {
416    /// The contained type.
417    type Inner;
418}
419
420impl<T> Container for Option<T> {
421    type Inner = T;
422}
423
424impl<T> Container for Vec<T> {
425    type Inner = T;
426}
427
428impl<T, const N: usize> Container for SmallVec<[T; N]> {
429    type Inner = T;
430}
431
432/// An uninhabitable type.
433#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
434pub enum Never {}
435
436impl Reflect for Never {
437    fn input() -> CastInfo {
438        CastInfo::Union(vec![])
439    }
440
441    fn output() -> CastInfo {
442        CastInfo::Union(vec![])
443    }
444
445    fn castable(_: &Value) -> bool {
446        false
447    }
448}
449
450impl IntoValue for Never {
451    fn into_value(self) -> Value {
452        match self {}
453    }
454}
455
456impl FromValue for Never {
457    fn from_value(value: Value) -> HintedStrResult<Self> {
458        Err(Self::error(&value))
459    }
460}
461
462cast! {
463    MathClass,
464    self => IntoValue::into_value(match self {
465        MathClass::Normal => "normal",
466        MathClass::Alphabetic => "alphabetic",
467        MathClass::Binary => "binary",
468        MathClass::Closing => "closing",
469        MathClass::Diacritic => "diacritic",
470        MathClass::Fence => "fence",
471        MathClass::GlyphPart => "glyph-part",
472        MathClass::Large => "large",
473        MathClass::Opening => "opening",
474        MathClass::Punctuation => "punctuation",
475        MathClass::Relation => "relation",
476        MathClass::Space => "space",
477        MathClass::Unary => "unary",
478        MathClass::Vary => "vary",
479        MathClass::Special => "special",
480    }),
481    /// The default class for non-special things.
482    "normal" => MathClass::Normal,
483    /// Punctuation, e.g. a comma.
484    "punctuation" => MathClass::Punctuation,
485    /// An opening delimiter, e.g. `(`.
486    "opening" => MathClass::Opening,
487    /// A closing delimiter, e.g. `)`.
488    "closing" => MathClass::Closing,
489    /// A delimiter that is the same on both sides, e.g. `|`.
490    "fence" => MathClass::Fence,
491    /// A large operator like `sum`.
492    "large" => MathClass::Large,
493    /// A relation like `=` or `prec`.
494    "relation" => MathClass::Relation,
495    /// A unary operator like `not`.
496    "unary" => MathClass::Unary,
497    /// A binary operator like `times`.
498    "binary" => MathClass::Binary,
499    /// An operator that can be both unary or binary like `+`.
500    "vary" => MathClass::Vary,
501}
502
503/// A type that contains a user-visible source portion and something that is
504/// derived from it, but not user-visible.
505///
506/// An example usage would be `source` being a `DataSource` and `derived` a
507/// TextMate theme parsed from it. With `Derived`, we can store both parts in
508/// the `RawElem::theme` field and get automatic nice `Reflect` and `IntoValue`
509/// impls.
510#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
511pub struct Derived<S, D> {
512    /// The source portion.
513    pub source: S,
514    /// The derived portion.
515    pub derived: D,
516}
517
518impl<S, D> Derived<S, D> {
519    /// Create a new instance from the `source` and the `derived` data.
520    pub fn new(source: S, derived: D) -> Self {
521        Self { source, derived }
522    }
523}
524
525impl<S: Reflect, D> Reflect for Derived<S, D> {
526    fn input() -> CastInfo {
527        S::input()
528    }
529
530    fn output() -> CastInfo {
531        S::output()
532    }
533
534    fn castable(value: &Value) -> bool {
535        S::castable(value)
536    }
537
538    fn error(found: &Value) -> HintedString {
539        S::error(found)
540    }
541}
542
543impl<S: IntoValue, D> IntoValue for Derived<S, D> {
544    fn into_value(self) -> Value {
545        self.source.into_value()
546    }
547}
548
549impl<S: Fold, D: Fold> Fold for Derived<S, D> {
550    fn fold(self, outer: Self) -> Self {
551        Self {
552            source: self.source.fold(outer.source),
553            derived: self.derived.fold(outer.derived),
554        }
555    }
556}