Skip to main content

typst_utils/
lib.rs

1//! Utilities for Typst.
2
3pub mod fat;
4
5#[macro_use]
6mod macros;
7mod bitset;
8mod deferred;
9mod duration;
10mod hash;
11mod listset;
12mod pico;
13mod protected;
14mod round;
15mod scalar;
16#[path = "version.rs"]
17mod version_;
18
19pub use self::bitset::{BitSet, SmallBitSet};
20pub use self::deferred::Deferred;
21pub use self::duration::format_duration;
22pub use self::hash::{HashLock, LazyHash, ManuallyHash, hash128};
23pub use self::listset::ListSet;
24pub use self::pico::{PicoStr, ResolvedPicoStr};
25pub use self::protected::Protected;
26pub use self::round::{round_int_with_precision, round_with_precision};
27pub use self::scalar::Scalar;
28pub use self::version_::{TypstVersion, display_commit, version};
29
30#[doc(hidden)]
31pub use once_cell;
32
33use std::fmt::{Debug, Display, Formatter};
34use std::hash::Hash;
35use std::iter::{Chain, Flatten, Rev};
36use std::num::{NonZeroU32, NonZeroUsize};
37use std::ops::{Add, Deref, DerefMut, Div, Mul, Neg, Sub};
38
39use smallvec::SmallVec;
40use unicode_math_class::MathClass;
41
42/// Turn a closure into a struct implementing [`Debug`].
43pub fn debug<F>(f: F) -> impl Debug
44where
45    F: Fn(&mut Formatter) -> std::fmt::Result,
46{
47    struct Wrapper<F>(F);
48
49    impl<F> Debug for Wrapper<F>
50    where
51        F: Fn(&mut Formatter) -> std::fmt::Result,
52    {
53        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
54            self.0(f)
55        }
56    }
57
58    Wrapper(f)
59}
60
61/// Turn a closure into a struct implementing [`Display`].
62pub fn display<F>(f: F) -> impl Display
63where
64    F: Fn(&mut Formatter) -> std::fmt::Result,
65{
66    struct Wrapper<F>(F);
67
68    impl<F> Display for Wrapper<F>
69    where
70        F: Fn(&mut Formatter) -> std::fmt::Result,
71    {
72        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
73            self.0(f)
74        }
75    }
76
77    Wrapper(f)
78}
79
80/// An extra constant for [`NonZeroUsize`].
81pub trait NonZeroExt {
82    /// The number `1`.
83    const ONE: Self;
84}
85
86impl NonZeroExt for NonZeroUsize {
87    const ONE: Self = Self::new(1).unwrap();
88}
89
90impl NonZeroExt for NonZeroU32 {
91    const ONE: Self = Self::new(1).unwrap();
92}
93
94/// Extra methods for [`Option`].
95pub trait OptionExt<T> {
96    /// Maps an `Option<T>` to `U` by applying a function to a contained value
97    /// (if `Some`) or returns a default (if `None`).
98    fn map_or_default<U: Default, F>(self, f: F) -> U
99    where
100        F: FnOnce(T) -> U;
101}
102
103impl<T> OptionExt<T> for Option<T> {
104    fn map_or_default<U: Default, F>(self, f: F) -> U
105    where
106        F: FnOnce(T) -> U,
107    {
108        match self {
109            Some(x) => f(x),
110            None => U::default(),
111        }
112    }
113}
114
115/// Extra methods for [`[T]`](slice).
116pub trait SliceExt<T> {
117    /// Returns a slice with all matching elements from the start of the slice
118    /// removed.
119    fn trim_start_matches<F>(&self, f: F) -> &[T]
120    where
121        F: FnMut(&T) -> bool;
122
123    /// Returns a slice with all matching elements from the end of the slice
124    /// removed.
125    fn trim_end_matches<F>(&self, f: F) -> &[T]
126    where
127        F: FnMut(&T) -> bool;
128
129    /// Split a slice into consecutive runs with the same key and yield for
130    /// each such run the key and the slice of elements with that key.
131    fn group_by_key<K, F>(&self, f: F) -> GroupByKey<'_, T, F>
132    where
133        F: FnMut(&T) -> K,
134        K: PartialEq;
135
136    /// Computes two indices which split a slice into three parts.
137    ///
138    /// - A prefix which matches `f`
139    /// - An inner portion
140    /// - A suffix which matches `f` and does not overlap with the prefix
141    ///
142    /// If all elements match `f`, the prefix becomes `self` and the suffix
143    /// will be empty.
144    ///
145    /// Returns the indices at which the inner portion and the suffix start.
146    fn split_prefix_suffix<F>(&self, f: F) -> (usize, usize)
147    where
148        F: FnMut(&T) -> bool;
149}
150
151impl<T> SliceExt<T> for [T] {
152    fn trim_start_matches<F>(&self, mut f: F) -> &[T]
153    where
154        F: FnMut(&T) -> bool,
155    {
156        let len = self.len();
157        let mut i = 0;
158        while i < len && f(&self[i]) {
159            i += 1;
160        }
161        &self[i..]
162    }
163
164    fn trim_end_matches<F>(&self, mut f: F) -> &[T]
165    where
166        F: FnMut(&T) -> bool,
167    {
168        let mut i = self.len();
169        while i > 0 && f(&self[i - 1]) {
170            i -= 1;
171        }
172        &self[..i]
173    }
174
175    fn group_by_key<K, F>(&self, f: F) -> GroupByKey<'_, T, F> {
176        GroupByKey { slice: self, f }
177    }
178
179    fn split_prefix_suffix<F>(&self, mut f: F) -> (usize, usize)
180    where
181        F: FnMut(&T) -> bool,
182    {
183        let start = self.iter().position(|v| !f(v)).unwrap_or(self.len());
184        let end = self
185            .iter()
186            .skip(start)
187            .rposition(|v| !f(v))
188            .map_or(start, |i| start + i + 1);
189        (start, end)
190    }
191}
192
193/// A variant of `dedup` that keeps the later value rather than the earlier one.
194pub trait Rdedup {
195    type Item;
196
197    /// Deduplicates values in a sorted sequence using a key function, but
198    /// unlike the standard version keeps the later one.
199    fn rdedup_by_key<K, F>(&mut self, key: F)
200    where
201        F: Fn(&mut Self::Item) -> K,
202        K: PartialEq<K>;
203}
204
205impl<T: Copy, const N: usize> Rdedup for SmallVec<[T; N]> {
206    type Item = T;
207
208    fn rdedup_by_key<K, F>(&mut self, mut key: F)
209    where
210        T: Copy,
211        K: PartialEq<K>,
212        F: FnMut(&mut T) -> K,
213    {
214        let mut k = 0;
215        for i in 1..self.len() {
216            if key(&mut self[i]) != key(&mut self[k]) {
217                k += 1;
218            }
219            if k < i {
220                self[k] = self[i];
221            }
222        }
223        self.truncate(k + 1);
224    }
225}
226
227/// This struct is created by [`SliceExt::group_by_key`].
228pub struct GroupByKey<'a, T, F> {
229    slice: &'a [T],
230    f: F,
231}
232
233impl<'a, T, K, F> Iterator for GroupByKey<'a, T, F>
234where
235    F: FnMut(&T) -> K,
236    K: PartialEq,
237{
238    type Item = (K, &'a [T]);
239
240    fn next(&mut self) -> Option<Self::Item> {
241        let mut iter = self.slice.iter();
242        let key = (self.f)(iter.next()?);
243        let count = 1 + iter.take_while(|t| (self.f)(t) == key).count();
244        let (head, tail) = self.slice.split_at(count);
245        self.slice = tail;
246        Some((key, head))
247    }
248}
249
250/// Adapter for reversing iterators conditionally.
251pub trait MaybeReverseIter {
252    type RevIfIter;
253
254    /// Reverse this iterator (apply .rev()) based on some condition.
255    fn rev_if(self, condition: bool) -> Self::RevIfIter
256    where
257        Self: Sized;
258}
259
260impl<I: Iterator + DoubleEndedIterator> MaybeReverseIter for I {
261    type RevIfIter =
262        Chain<Flatten<std::option::IntoIter<I>>, Flatten<std::option::IntoIter<Rev<I>>>>;
263
264    fn rev_if(self, condition: bool) -> Self::RevIfIter
265    where
266        Self: Sized,
267    {
268        let (maybe_self_iter, maybe_rev_iter) =
269            if condition { (None, Some(self.rev())) } else { (Some(self), None) };
270
271        maybe_self_iter
272            .into_iter()
273            .flatten()
274            .chain(maybe_rev_iter.into_iter().flatten())
275    }
276}
277
278/// Check if the [`Option`]-wrapped L is same to R.
279pub fn option_eq<L, R>(left: Option<L>, other: R) -> bool
280where
281    L: PartialEq<R>,
282{
283    left.is_some_and(|v| v == other)
284}
285
286/// A container around a static reference that is cheap to clone and hash.
287#[derive(Debug)]
288pub struct Static<T: 'static>(pub &'static T);
289
290impl<T> Deref for Static<T> {
291    type Target = T;
292
293    fn deref(&self) -> &Self::Target {
294        self.0
295    }
296}
297
298impl<T> Copy for Static<T> {}
299
300impl<T> Clone for Static<T> {
301    fn clone(&self) -> Self {
302        *self
303    }
304}
305
306impl<T> Eq for Static<T> {}
307
308impl<T> PartialEq for Static<T> {
309    fn eq(&self, other: &Self) -> bool {
310        std::ptr::eq(self.0, other.0)
311    }
312}
313
314impl<T> Hash for Static<T> {
315    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
316        state.write_usize(self.0 as *const _ as _);
317    }
318}
319
320/// Generic access to a structure's components.
321pub trait Get<Index> {
322    /// The structure's component type.
323    type Component;
324
325    /// Borrow the component for the specified index.
326    fn get_ref(&self, index: Index) -> &Self::Component;
327
328    /// Borrow the component for the specified index mutably.
329    fn get_mut(&mut self, index: Index) -> &mut Self::Component;
330
331    /// Convenience method for getting a copy of a component.
332    fn get(self, index: Index) -> Self::Component
333    where
334        Self: Sized,
335        Self::Component: Copy,
336    {
337        *self.get_ref(index)
338    }
339
340    /// Convenience method for setting a component.
341    fn set(&mut self, index: Index, component: Self::Component) {
342        *self.get_mut(index) = component;
343    }
344
345    /// Builder-style method for setting a component.
346    fn with(mut self, index: Index, component: Self::Component) -> Self
347    where
348        Self: Sized,
349    {
350        self.set(index, component);
351        self
352    }
353}
354
355/// A numeric type.
356pub trait Numeric:
357    Sized
358    + Debug
359    + Copy
360    + PartialEq
361    + Neg<Output = Self>
362    + Add<Output = Self>
363    + Sub<Output = Self>
364    + Mul<f64, Output = Self>
365    + Div<f64, Output = Self>
366{
367    /// The identity element for addition.
368    fn zero() -> Self;
369
370    /// Whether `self` is zero.
371    fn is_zero(self) -> bool {
372        self == Self::zero()
373    }
374
375    /// Whether `self` consists only of finite parts.
376    fn is_finite(self) -> bool;
377}
378
379/// A marker trait for numeric lengths.
380pub trait NumericLength: Numeric {}
381
382/// Returns the default math class of a character in Typst, if it has one.
383///
384/// This is determined by the Unicode math class, with some manual overrides.
385pub fn default_math_class(c: char) -> Option<MathClass> {
386    match c {
387        // Better spacing.
388        // https://github.com/typst/typst/commit/2e039cb052fcb768027053cbf02ce396f6d7a6be
389        ':' => Some(MathClass::Relation),
390
391        // Better spacing when used alongside + PLUS SIGN.
392        // https://github.com/typst/typst/pull/1726
393        '⋯' | '⋱' | '⋰' | '⋮' => Some(MathClass::Normal),
394
395        // Better spacing.
396        // https://github.com/typst/typst/pull/1855
397        '.' | '/' => Some(MathClass::Normal),
398
399        // ⊥ UP TACK should not be a relation, contrary to ⟂ PERPENDICULAR.
400        // https://github.com/typst/typst/pull/5714
401        '\u{22A5}' => Some(MathClass::Normal),
402
403        // Used as a binary connector in linear logic, where it is referred to
404        // as "par".
405        // https://github.com/typst/typst/issues/5764
406        '⅋' => Some(MathClass::Binary),
407
408        // Those overrides should become the default in the next revision of
409        // MathClass.txt.
410        // https://github.com/typst/typst/issues/5764#issuecomment-2632435247
411        '⎰' | '⟅' => Some(MathClass::Opening),
412        '⎱' | '⟆' => Some(MathClass::Closing),
413
414        // Both ∨ and ⟑ are classified as Binary.
415        // https://github.com/typst/typst/issues/5764
416        '⟇' => Some(MathClass::Binary),
417
418        // Arabic comma.
419        // https://github.com/latex3/unicode-math/pull/633#issuecomment-2028936135
420        '،' => Some(MathClass::Punctuation),
421
422        c => unicode_math_class::class(c),
423    }
424}
425
426/// Automatically calls a deferred function when the returned handle is dropped.
427pub fn defer<T, F: FnOnce(&mut T)>(
428    thing: &mut T,
429    deferred: F,
430) -> impl DerefMut<Target = T> {
431    pub struct DeferHandle<'a, T, F: FnOnce(&mut T)> {
432        thing: &'a mut T,
433        deferred: Option<F>,
434    }
435
436    impl<'a, T, F: FnOnce(&mut T)> Drop for DeferHandle<'a, T, F> {
437        fn drop(&mut self) {
438            std::mem::take(&mut self.deferred).expect("deferred function")(self.thing);
439        }
440    }
441
442    impl<T, F: FnOnce(&mut T)> std::ops::Deref for DeferHandle<'_, T, F> {
443        type Target = T;
444
445        fn deref(&self) -> &Self::Target {
446            self.thing
447        }
448    }
449
450    impl<T, F: FnOnce(&mut T)> std::ops::DerefMut for DeferHandle<'_, T, F> {
451        fn deref_mut(&mut self) -> &mut Self::Target {
452            self.thing
453        }
454    }
455
456    DeferHandle { thing, deferred: Some(deferred) }
457}
458
459/// Describes the source code where something is defined.
460///
461/// This does not include a concrete line number because, due to the way Rust
462/// macros expand, a `#[func]` item in a `#[scope]` impl receives the line of
463/// the full impl, which is not so useful. Rather, a per-file unique key is used
464/// to find the element. Identifiers of a semantical parent may also be used in
465/// this key. This has the added benefit that we can reliably find the
466/// definition site in the presence of edits (for hot reload).
467#[derive(Debug, Copy, Clone, Eq, PartialEq)]
468pub struct DefSite {
469    /// The path to the file as returned by the `file!()` macro.
470    ///
471    /// Note that the path separator may vary across platforms.
472    pub path: &'static str,
473    /// An identifying key associated with the definition. Can be used to find
474    /// the definition in the file.
475    pub key: &'static str,
476}
477
478/// Implements `Display` for a type that implements clap's `ValueEnum` via
479/// `to_possible_values`.
480#[macro_export]
481macro_rules! display_possible_values {
482    ($ty:ty) => {
483        impl std::fmt::Display for $ty {
484            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485                self.to_possible_value()
486                    .expect("no values are skipped")
487                    .get_name()
488                    .fmt(f)
489            }
490        }
491    };
492}
493
494#[cfg(test)]
495mod tests {
496    use super::*;
497
498    #[test]
499    fn test_rdedup() {
500        #[track_caller]
501        fn test(given: &[(char, i32)], expected: &[(char, i32)]) {
502            let mut vec: SmallVec<[(char, i32); 2]> = given.into();
503            vec.rdedup_by_key(|&mut (c, _)| c);
504            assert_eq!(vec.as_slice(), expected);
505        }
506
507        test(&[], &[]);
508        test(&[('a', 1), ('a', 2), ('a', 3), ('b', 2)], &[('a', 3), ('b', 2)]);
509        test(&[('b', 2), ('c', 3), ('c', 4)], &[('b', 2), ('c', 4)]);
510        test(
511            &[('a', 1), ('b', 1), ('c', 1), ('c', 2), ('d', 1)],
512            &[('a', 1), ('b', 1), ('c', 2), ('d', 1)],
513        );
514    }
515}