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 pico;
12mod round;
13mod scalar;
14
15pub use self::bitset::{BitSet, SmallBitSet};
16pub use self::deferred::Deferred;
17pub use self::duration::format_duration;
18pub use self::hash::{LazyHash, ManuallyHash};
19pub use self::pico::{PicoStr, ResolvedPicoStr};
20pub use self::round::{round_int_with_precision, round_with_precision};
21pub use self::scalar::Scalar;
22
23#[doc(hidden)]
24pub use once_cell;
25
26use std::fmt::{Debug, Formatter};
27use std::hash::Hash;
28use std::iter::{Chain, Flatten, Rev};
29use std::num::NonZeroUsize;
30use std::ops::{Add, Deref, Div, Mul, Neg, Sub};
31use std::sync::Arc;
32
33use siphasher::sip128::{Hasher128, SipHasher13};
34use unicode_math_class::MathClass;
35
36/// Turn a closure into a struct implementing [`Debug`].
37pub fn debug<F>(f: F) -> impl Debug
38where
39    F: Fn(&mut Formatter) -> std::fmt::Result,
40{
41    struct Wrapper<F>(F);
42
43    impl<F> Debug for Wrapper<F>
44    where
45        F: Fn(&mut Formatter) -> std::fmt::Result,
46    {
47        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48            self.0(f)
49        }
50    }
51
52    Wrapper(f)
53}
54
55/// Calculate a 128-bit siphash of a value.
56pub fn hash128<T: Hash + ?Sized>(value: &T) -> u128 {
57    let mut state = SipHasher13::new();
58    value.hash(&mut state);
59    state.finish128().as_u128()
60}
61
62/// An extra constant for [`NonZeroUsize`].
63pub trait NonZeroExt {
64    /// The number `1`.
65    const ONE: Self;
66}
67
68impl NonZeroExt for NonZeroUsize {
69    const ONE: Self = match Self::new(1) {
70        Some(v) => v,
71        None => unreachable!(),
72    };
73}
74
75/// Extra methods for [`Arc`].
76pub trait ArcExt<T> {
77    /// Takes the inner value if there is exactly one strong reference and
78    /// clones it otherwise.
79    fn take(self) -> T;
80}
81
82impl<T: Clone> ArcExt<T> for Arc<T> {
83    fn take(self) -> T {
84        match Arc::try_unwrap(self) {
85            Ok(v) => v,
86            Err(rc) => (*rc).clone(),
87        }
88    }
89}
90
91/// Extra methods for [`Option`].
92pub trait OptionExt<T> {
93    /// Maps an `Option<T>` to `U` by applying a function to a contained value
94    /// (if `Some`) or returns a default (if `None`).
95    fn map_or_default<U: Default, F>(self, f: F) -> U
96    where
97        F: FnOnce(T) -> U;
98}
99
100impl<T> OptionExt<T> for Option<T> {
101    fn map_or_default<U: Default, F>(self, f: F) -> U
102    where
103        F: FnOnce(T) -> U,
104    {
105        match self {
106            Some(x) => f(x),
107            None => U::default(),
108        }
109    }
110}
111
112/// Extra methods for [`[T]`](slice).
113pub trait SliceExt<T> {
114    /// Returns a slice with all matching elements from the start of the slice
115    /// removed.
116    fn trim_start_matches<F>(&self, f: F) -> &[T]
117    where
118        F: FnMut(&T) -> bool;
119
120    /// Returns a slice with all matching elements from the end of the slice
121    /// removed.
122    fn trim_end_matches<F>(&self, f: F) -> &[T]
123    where
124        F: FnMut(&T) -> bool;
125
126    /// Split a slice into consecutive runs with the same key and yield for
127    /// each such run the key and the slice of elements with that key.
128    fn group_by_key<K, F>(&self, f: F) -> GroupByKey<'_, T, F>
129    where
130        F: FnMut(&T) -> K,
131        K: PartialEq;
132
133    /// Computes two indices which split a slice into three parts.
134    ///
135    /// - A prefix which matches `f`
136    /// - An inner portion
137    /// - A suffix which matches `f` and does not overlap with the prefix
138    ///
139    /// If all elements match `f`, the prefix becomes `self` and the suffix
140    /// will be empty.
141    ///
142    /// Returns the indices at which the inner portion and the suffix start.
143    fn split_prefix_suffix<F>(&self, f: F) -> (usize, usize)
144    where
145        F: FnMut(&T) -> bool;
146}
147
148impl<T> SliceExt<T> for [T] {
149    fn trim_start_matches<F>(&self, mut f: F) -> &[T]
150    where
151        F: FnMut(&T) -> bool,
152    {
153        let len = self.len();
154        let mut i = 0;
155        while i < len && f(&self[i]) {
156            i += 1;
157        }
158        &self[i..]
159    }
160
161    fn trim_end_matches<F>(&self, mut f: F) -> &[T]
162    where
163        F: FnMut(&T) -> bool,
164    {
165        let mut i = self.len();
166        while i > 0 && f(&self[i - 1]) {
167            i -= 1;
168        }
169        &self[..i]
170    }
171
172    fn group_by_key<K, F>(&self, f: F) -> GroupByKey<'_, T, F> {
173        GroupByKey { slice: self, f }
174    }
175
176    fn split_prefix_suffix<F>(&self, mut f: F) -> (usize, usize)
177    where
178        F: FnMut(&T) -> bool,
179    {
180        let start = self.iter().position(|v| !f(v)).unwrap_or(self.len());
181        let end = self
182            .iter()
183            .skip(start)
184            .rposition(|v| !f(v))
185            .map_or(start, |i| start + i + 1);
186        (start, end)
187    }
188}
189
190/// This struct is created by [`SliceExt::group_by_key`].
191pub struct GroupByKey<'a, T, F> {
192    slice: &'a [T],
193    f: F,
194}
195
196impl<'a, T, K, F> Iterator for GroupByKey<'a, T, F>
197where
198    F: FnMut(&T) -> K,
199    K: PartialEq,
200{
201    type Item = (K, &'a [T]);
202
203    fn next(&mut self) -> Option<Self::Item> {
204        let mut iter = self.slice.iter();
205        let key = (self.f)(iter.next()?);
206        let count = 1 + iter.take_while(|t| (self.f)(t) == key).count();
207        let (head, tail) = self.slice.split_at(count);
208        self.slice = tail;
209        Some((key, head))
210    }
211}
212
213/// Adapter for reversing iterators conditionally.
214pub trait MaybeReverseIter {
215    type RevIfIter;
216
217    /// Reverse this iterator (apply .rev()) based on some condition.
218    fn rev_if(self, condition: bool) -> Self::RevIfIter
219    where
220        Self: Sized;
221}
222
223impl<I: Iterator + DoubleEndedIterator> MaybeReverseIter for I {
224    type RevIfIter =
225        Chain<Flatten<std::option::IntoIter<I>>, Flatten<std::option::IntoIter<Rev<I>>>>;
226
227    fn rev_if(self, condition: bool) -> Self::RevIfIter
228    where
229        Self: Sized,
230    {
231        let (maybe_self_iter, maybe_rev_iter) =
232            if condition { (None, Some(self.rev())) } else { (Some(self), None) };
233
234        maybe_self_iter
235            .into_iter()
236            .flatten()
237            .chain(maybe_rev_iter.into_iter().flatten())
238    }
239}
240
241/// Check if the [`Option`]-wrapped L is same to R.
242pub fn option_eq<L, R>(left: Option<L>, other: R) -> bool
243where
244    L: PartialEq<R>,
245{
246    left.is_some_and(|v| v == other)
247}
248
249/// A container around a static reference that is cheap to clone and hash.
250#[derive(Debug)]
251pub struct Static<T: 'static>(pub &'static T);
252
253impl<T> Deref for Static<T> {
254    type Target = T;
255
256    fn deref(&self) -> &Self::Target {
257        self.0
258    }
259}
260
261impl<T> Copy for Static<T> {}
262
263impl<T> Clone for Static<T> {
264    fn clone(&self) -> Self {
265        *self
266    }
267}
268
269impl<T> Eq for Static<T> {}
270
271impl<T> PartialEq for Static<T> {
272    fn eq(&self, other: &Self) -> bool {
273        std::ptr::eq(self.0, other.0)
274    }
275}
276
277impl<T> Hash for Static<T> {
278    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
279        state.write_usize(self.0 as *const _ as _);
280    }
281}
282
283/// Generic access to a structure's components.
284pub trait Get<Index> {
285    /// The structure's component type.
286    type Component;
287
288    /// Borrow the component for the specified index.
289    fn get_ref(&self, index: Index) -> &Self::Component;
290
291    /// Borrow the component for the specified index mutably.
292    fn get_mut(&mut self, index: Index) -> &mut Self::Component;
293
294    /// Convenience method for getting a copy of a component.
295    fn get(self, index: Index) -> Self::Component
296    where
297        Self: Sized,
298        Self::Component: Copy,
299    {
300        *self.get_ref(index)
301    }
302
303    /// Convenience method for setting a component.
304    fn set(&mut self, index: Index, component: Self::Component) {
305        *self.get_mut(index) = component;
306    }
307
308    /// Builder-style method for setting a component.
309    fn with(mut self, index: Index, component: Self::Component) -> Self
310    where
311        Self: Sized,
312    {
313        self.set(index, component);
314        self
315    }
316}
317
318/// A numeric type.
319pub trait Numeric:
320    Sized
321    + Debug
322    + Copy
323    + PartialEq
324    + Neg<Output = Self>
325    + Add<Output = Self>
326    + Sub<Output = Self>
327    + Mul<f64, Output = Self>
328    + Div<f64, Output = Self>
329{
330    /// The identity element for addition.
331    fn zero() -> Self;
332
333    /// Whether `self` is zero.
334    fn is_zero(self) -> bool {
335        self == Self::zero()
336    }
337
338    /// Whether `self` consists only of finite parts.
339    fn is_finite(self) -> bool;
340}
341
342/// Returns the default math class of a character in Typst, if it has one.
343///
344/// This is determined by the Unicode math class, with some manual overrides.
345pub fn default_math_class(c: char) -> Option<MathClass> {
346    match c {
347        // Better spacing.
348        // https://github.com/typst/typst/commit/2e039cb052fcb768027053cbf02ce396f6d7a6be
349        ':' => Some(MathClass::Relation),
350
351        // Better spacing when used alongside + PLUS SIGN.
352        // https://github.com/typst/typst/pull/1726
353        '⋯' | '⋱' | '⋰' | '⋮' => Some(MathClass::Normal),
354
355        // Better spacing.
356        // https://github.com/typst/typst/pull/1855
357        '.' | '/' => Some(MathClass::Normal),
358
359        // ⊥ UP TACK should not be a relation, contrary to ⟂ PERPENDICULAR.
360        // https://github.com/typst/typst/pull/5714
361        '\u{22A5}' => Some(MathClass::Normal),
362
363        c => unicode_math_class::class(c),
364    }
365}