typst_library/foundations/
cast.rs1#[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, SyntaxMode};
13use unicode_math_class::MathClass;
14
15use crate::diag::{At, HintedStrResult, HintedString, SourceResult, StrResult};
16use crate::foundations::{
17 Fold, NativeElement, Packed, Repr, Str, Type, Value, array, repr,
18};
19
20pub trait Reflect {
34 fn input() -> CastInfo;
36
37 fn output() -> CastInfo;
39
40 fn castable(value: &Value) -> bool;
46
47 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
172pub trait IntoValue {
176 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
210pub trait IntoResult {
215 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
249pub trait FromValue<V = Value>: Sized + Reflect {
253 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#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
291pub enum CastInfo {
292 Any,
294 Value(Value, &'static str),
296 Type(Type),
298 Union(Vec<Self>),
300}
301
302impl CastInfo {
303 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 && !matching_type
352 && parts.iter().any(|p| p == "float")
353 {
354 msg.hint(eco_format!(
355 "if loss of precision is acceptable, explicitly cast the \
356 decimal to a float with `float(value)`"
357 ));
358 }
359
360 msg
361 }
362
363 pub fn walk<F>(&self, mut f: F)
365 where
366 F: FnMut(&Self),
367 {
368 fn inner<F>(info: &CastInfo, f: &mut F)
369 where
370 F: FnMut(&CastInfo),
371 {
372 if let CastInfo::Union(infos) = info {
373 for child in infos {
374 inner(child, f);
375 }
376 } else {
377 f(info);
378 }
379 }
380
381 inner(self, &mut f)
382 }
383}
384
385impl Add for CastInfo {
386 type Output = Self;
387
388 fn add(self, rhs: Self) -> Self {
389 Self::Union(match (self, rhs) {
390 (Self::Union(mut lhs), Self::Union(rhs)) => {
391 for cast in rhs {
392 if !lhs.contains(&cast) {
393 lhs.push(cast);
394 }
395 }
396 lhs
397 }
398 (Self::Union(mut lhs), rhs) => {
399 if !lhs.contains(&rhs) {
400 lhs.push(rhs);
401 }
402 lhs
403 }
404 (lhs, Self::Union(mut rhs)) => {
405 if !rhs.contains(&lhs) {
406 rhs.insert(0, lhs);
407 }
408 rhs
409 }
410 (lhs, rhs) => vec![lhs, rhs],
411 })
412 }
413}
414
415pub trait Container {
417 type Inner;
419}
420
421impl<T> Container for Option<T> {
422 type Inner = T;
423}
424
425impl<T> Container for Vec<T> {
426 type Inner = T;
427}
428
429impl<T, const N: usize> Container for SmallVec<[T; N]> {
430 type Inner = T;
431}
432
433#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
435pub enum Never {}
436
437impl Reflect for Never {
438 fn input() -> CastInfo {
439 CastInfo::Union(vec![])
440 }
441
442 fn output() -> CastInfo {
443 CastInfo::Union(vec![])
444 }
445
446 fn castable(_: &Value) -> bool {
447 false
448 }
449}
450
451impl IntoValue for Never {
452 fn into_value(self) -> Value {
453 match self {}
454 }
455}
456
457impl FromValue for Never {
458 fn from_value(value: Value) -> HintedStrResult<Self> {
459 Err(Self::error(&value))
460 }
461}
462
463cast! {
464 SyntaxMode,
465 self => IntoValue::into_value(match self {
466 SyntaxMode::Markup => "markup",
467 SyntaxMode::Math => "math",
468 SyntaxMode::Code => "code",
469 }),
470 "markup" => SyntaxMode::Markup,
472 "math" => SyntaxMode::Math,
474 "code" => SyntaxMode::Code,
476}
477
478cast! {
479 MathClass,
480 self => IntoValue::into_value(match self {
481 MathClass::Normal => "normal",
482 MathClass::Alphabetic => "alphabetic",
483 MathClass::Binary => "binary",
484 MathClass::Closing => "closing",
485 MathClass::Diacritic => "diacritic",
486 MathClass::Fence => "fence",
487 MathClass::GlyphPart => "glyph-part",
488 MathClass::Large => "large",
489 MathClass::Opening => "opening",
490 MathClass::Punctuation => "punctuation",
491 MathClass::Relation => "relation",
492 MathClass::Space => "space",
493 MathClass::Unary => "unary",
494 MathClass::Vary => "vary",
495 MathClass::Special => "special",
496 }),
497 "normal" => MathClass::Normal,
499 "punctuation" => MathClass::Punctuation,
501 "opening" => MathClass::Opening,
503 "closing" => MathClass::Closing,
505 "fence" => MathClass::Fence,
507 "large" => MathClass::Large,
509 "relation" => MathClass::Relation,
511 "unary" => MathClass::Unary,
513 "binary" => MathClass::Binary,
515 "vary" => MathClass::Vary,
517}
518
519#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
527pub struct Derived<S, D> {
528 pub source: S,
530 pub derived: D,
532}
533
534impl<S, D> Derived<S, D> {
535 pub fn new(source: S, derived: D) -> Self {
537 Self { source, derived }
538 }
539}
540
541impl<S: Reflect, D> Reflect for Derived<S, D> {
542 fn input() -> CastInfo {
543 S::input()
544 }
545
546 fn output() -> CastInfo {
547 S::output()
548 }
549
550 fn castable(value: &Value) -> bool {
551 S::castable(value)
552 }
553
554 fn error(found: &Value) -> HintedString {
555 S::error(found)
556 }
557}
558
559impl<S: IntoValue, D> IntoValue for Derived<S, D> {
560 fn into_value(self) -> Value {
561 self.source.into_value()
562 }
563}
564
565impl<S: Fold, D: Fold> Fold for Derived<S, D> {
566 fn fold(self, outer: Self) -> Self {
567 Self {
568 source: self.source.fold(outer.source),
569 derived: self.derived.fold(outer.derived),
570 }
571 }
572}