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};
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
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, Hash, PartialOrd)]
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 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 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
414pub trait Container {
416 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#[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 "normal" => MathClass::Normal,
483 "punctuation" => MathClass::Punctuation,
485 "opening" => MathClass::Opening,
487 "closing" => MathClass::Closing,
489 "fence" => MathClass::Fence,
491 "large" => MathClass::Large,
493 "relation" => MathClass::Relation,
495 "unary" => MathClass::Unary,
497 "binary" => MathClass::Binary,
499 "vary" => MathClass::Vary,
501}
502
503#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
511pub struct Derived<S, D> {
512 pub source: S,
514 pub derived: D,
516}
517
518impl<S, D> Derived<S, D> {
519 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}