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 let mut span = Span::detached();
266 if let Value::Content(content) = value {
267 match content.into_packed::<T>() {
268 Ok(packed) => return Ok(packed),
269 Err(content) => {
270 span = content.span();
271 value = Value::Content(content)
272 }
273 }
274 }
275 let val = T::from_value(value)?;
276 Ok(Packed::new(val).spanned(span))
277 }
278}
279
280impl<T: FromValue> FromValue<Spanned<Value>> for T {
281 fn from_value(value: Spanned<Value>) -> HintedStrResult<Self> {
282 T::from_value(value.v)
283 }
284}
285
286impl<T: FromValue> FromValue<Spanned<Value>> for Spanned<T> {
287 fn from_value(value: Spanned<Value>) -> HintedStrResult<Self> {
288 let span = value.span;
289 T::from_value(value.v).map(|t| Spanned::new(t, span))
290 }
291}
292
293#[derive(Debug, Clone, PartialEq, PartialOrd, Hash)]
295pub enum CastInfo {
296 Any,
298 Value(Value, &'static str),
300 Type(Type),
302 Union(Vec<Self>),
304}
305
306impl CastInfo {
307 pub fn error(&self, found: &Value) -> HintedString {
310 let mut matching_type = false;
311 let mut parts = vec![];
312
313 self.walk(|info| match info {
314 CastInfo::Any => parts.push("anything".into()),
315 CastInfo::Value(value, _) => {
316 parts.push(value.repr());
317 if value.ty() == found.ty() {
318 matching_type = true;
319 }
320 }
321 CastInfo::Type(ty) => parts.push(eco_format!("{ty}")),
322 CastInfo::Union(_) => {}
323 });
324
325 let mut msg = String::from("expected ");
326 if parts.is_empty() {
327 msg.push_str(" nothing");
328 }
329
330 msg.push_str(&repr::separated_list(&parts, "or"));
331
332 if !matching_type {
333 msg.push_str(", found ");
334 write!(msg, "{}", found.ty()).unwrap();
335 }
336
337 let mut msg: HintedString = msg.into();
338
339 if let Value::Int(i) = found {
340 if !matching_type && parts.iter().any(|p| p == "length") {
341 msg.hint(eco_format!("a length needs a unit - did you mean {i}pt?"));
342 }
343 } else if let Value::Str(s) = found {
344 if !matching_type && parts.iter().any(|p| p == "label") {
345 if typst_syntax::is_valid_label_literal_id(s) {
346 msg.hint(eco_format!(
347 "use `<{s}>` or `label({})` to create a label",
348 s.repr()
349 ));
350 } else {
351 msg.hint(eco_format!("use `label({})` to create a label", s.repr()));
352 }
353 }
354 } else if let Value::Decimal(_) = found
355 && !matching_type
356 && parts.iter().any(|p| p == "float")
357 {
358 msg.hint(eco_format!(
359 "if loss of precision is acceptable, explicitly cast the \
360 decimal to a float with `float(value)`"
361 ));
362 }
363
364 msg
365 }
366
367 pub fn walk<F>(&self, mut f: F)
369 where
370 F: FnMut(&Self),
371 {
372 fn inner<F>(info: &CastInfo, f: &mut F)
373 where
374 F: FnMut(&CastInfo),
375 {
376 if let CastInfo::Union(infos) = info {
377 for child in infos {
378 inner(child, f);
379 }
380 } else {
381 f(info);
382 }
383 }
384
385 inner(self, &mut f)
386 }
387}
388
389impl Add for CastInfo {
390 type Output = Self;
391
392 fn add(self, rhs: Self) -> Self {
393 Self::Union(match (self, rhs) {
394 (Self::Union(mut lhs), Self::Union(rhs)) => {
395 for cast in rhs {
396 if !lhs.contains(&cast) {
397 lhs.push(cast);
398 }
399 }
400 lhs
401 }
402 (Self::Union(mut lhs), rhs) => {
403 if !lhs.contains(&rhs) {
404 lhs.push(rhs);
405 }
406 lhs
407 }
408 (lhs, Self::Union(mut rhs)) => {
409 if !rhs.contains(&lhs) {
410 rhs.insert(0, lhs);
411 }
412 rhs
413 }
414 (lhs, rhs) => vec![lhs, rhs],
415 })
416 }
417}
418
419pub trait Container {
421 type Inner;
423}
424
425impl<T> Container for Option<T> {
426 type Inner = T;
427}
428
429impl<T> Container for Vec<T> {
430 type Inner = T;
431}
432
433impl<T, const N: usize> Container for SmallVec<[T; N]> {
434 type Inner = T;
435}
436
437#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
439pub enum Never {}
440
441impl Reflect for Never {
442 fn input() -> CastInfo {
443 CastInfo::Union(vec![])
444 }
445
446 fn output() -> CastInfo {
447 CastInfo::Union(vec![])
448 }
449
450 fn castable(_: &Value) -> bool {
451 false
452 }
453}
454
455impl IntoValue for Never {
456 fn into_value(self) -> Value {
457 match self {}
458 }
459}
460
461impl FromValue for Never {
462 fn from_value(value: Value) -> HintedStrResult<Self> {
463 Err(Self::error(&value))
464 }
465}
466
467cast! {
468 SyntaxMode,
469 self => IntoValue::into_value(match self {
470 SyntaxMode::Markup => "markup",
471 SyntaxMode::Math => "math",
472 SyntaxMode::Code => "code",
473 }),
474 "markup" => SyntaxMode::Markup,
476 "math" => SyntaxMode::Math,
478 "code" => SyntaxMode::Code,
480}
481
482cast! {
483 MathClass,
484 self => IntoValue::into_value(match self {
485 MathClass::Normal => "normal",
486 MathClass::Alphabetic => "alphabetic",
487 MathClass::Binary => "binary",
488 MathClass::Closing => "closing",
489 MathClass::Diacritic => "diacritic",
490 MathClass::Fence => "fence",
491 MathClass::GlyphPart => "glyph-part",
492 MathClass::Large => "large",
493 MathClass::Opening => "opening",
494 MathClass::Punctuation => "punctuation",
495 MathClass::Relation => "relation",
496 MathClass::Space => "space",
497 MathClass::Unary => "unary",
498 MathClass::Vary => "vary",
499 MathClass::Special => "special",
500 }),
501 "normal" => MathClass::Normal,
503 "punctuation" => MathClass::Punctuation,
505 "opening" => MathClass::Opening,
507 "closing" => MathClass::Closing,
509 "fence" => MathClass::Fence,
511 "large" => MathClass::Large,
513 "relation" => MathClass::Relation,
515 "unary" => MathClass::Unary,
517 "binary" => MathClass::Binary,
519 "vary" => MathClass::Vary,
521}
522
523#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
531pub struct Derived<S, D> {
532 pub source: S,
534 pub derived: D,
536}
537
538impl<S, D> Derived<S, D> {
539 pub fn new(source: S, derived: D) -> Self {
541 Self { source, derived }
542 }
543}
544
545impl<S: Reflect, D> Reflect for Derived<S, D> {
546 fn input() -> CastInfo {
547 S::input()
548 }
549
550 fn output() -> CastInfo {
551 S::output()
552 }
553
554 fn castable(value: &Value) -> bool {
555 S::castable(value)
556 }
557
558 fn error(found: &Value) -> HintedString {
559 S::error(found)
560 }
561}
562
563impl<S: IntoValue, D> IntoValue for Derived<S, D> {
564 fn into_value(self) -> Value {
565 self.source.into_value()
566 }
567}
568
569impl<S: Fold, D: Fold> Fold for Derived<S, D> {
570 fn fold(self, outer: Self) -> Self {
571 Self {
572 source: self.source.fold(outer.source),
573 derived: self.derived.fold(outer.derived),
574 }
575 }
576}