//! Utilities for Typst. pub mod fat; #[macro_use] mod macros; mod bitset; mod deferred; mod duration; mod hash; mod pico; mod round; mod scalar; pub use self::bitset::{BitSet, SmallBitSet}; pub use self::deferred::Deferred; pub use self::duration::format_duration; pub use self::hash::{LazyHash, ManuallyHash}; pub use self::pico::{PicoStr, ResolvedPicoStr}; pub use self::round::{round_int_with_precision, round_with_precision}; pub use self::scalar::Scalar; #[doc(hidden)] pub use once_cell; use std::fmt::{Debug, Formatter}; use std::hash::Hash; use std::iter::{Chain, Flatten, Rev}; use std::num::NonZeroUsize; use std::ops::{Add, Deref, Div, Mul, Neg, Sub}; use std::sync::Arc; use siphasher::sip128::{Hasher128, SipHasher13}; use unicode_math_class::MathClass; /// Turn a closure into a struct implementing [`Debug`]. pub fn debug(f: F) -> impl Debug where F: Fn(&mut Formatter) -> std::fmt::Result, { struct Wrapper(F); impl Debug for Wrapper where F: Fn(&mut Formatter) -> std::fmt::Result, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0(f) } } Wrapper(f) } /// Calculate a 128-bit siphash of a value. pub fn hash128(value: &T) -> u128 { let mut state = SipHasher13::new(); value.hash(&mut state); state.finish128().as_u128() } /// An extra constant for [`NonZeroUsize`]. pub trait NonZeroExt { /// The number `1`. const ONE: Self; } impl NonZeroExt for NonZeroUsize { const ONE: Self = match Self::new(1) { Some(v) => v, None => unreachable!(), }; } /// Extra methods for [`Arc`]. pub trait ArcExt { /// Takes the inner value if there is exactly one strong reference and /// clones it otherwise. fn take(self) -> T; } impl ArcExt for Arc { fn take(self) -> T { match Arc::try_unwrap(self) { Ok(v) => v, Err(rc) => (*rc).clone(), } } } /// Extra methods for [`Option`]. pub trait OptionExt { /// Maps an `Option` to `U` by applying a function to a contained value /// (if `Some`) or returns a default (if `None`). fn map_or_default(self, f: F) -> U where F: FnOnce(T) -> U; } impl OptionExt for Option { fn map_or_default(self, f: F) -> U where F: FnOnce(T) -> U, { match self { Some(x) => f(x), None => U::default(), } } } /// Extra methods for [`[T]`](slice). pub trait SliceExt { /// Returns a slice with all matching elements from the start of the slice /// removed. fn trim_start_matches(&self, f: F) -> &[T] where F: FnMut(&T) -> bool; /// Returns a slice with all matching elements from the end of the slice /// removed. fn trim_end_matches(&self, f: F) -> &[T] where F: FnMut(&T) -> bool; /// Split a slice into consecutive runs with the same key and yield for /// each such run the key and the slice of elements with that key. fn group_by_key(&self, f: F) -> GroupByKey<'_, T, F> where F: FnMut(&T) -> K, K: PartialEq; /// Computes two indices which split a slice into three parts. /// /// - A prefix which matches `f` /// - An inner portion /// - A suffix which matches `f` and does not overlap with the prefix /// /// If all elements match `f`, the prefix becomes `self` and the suffix /// will be empty. /// /// Returns the indices at which the inner portion and the suffix start. fn split_prefix_suffix(&self, f: F) -> (usize, usize) where F: FnMut(&T) -> bool; } impl SliceExt for [T] { fn trim_start_matches(&self, mut f: F) -> &[T] where F: FnMut(&T) -> bool, { let len = self.len(); let mut i = 0; while i < len && f(&self[i]) { i += 1; } &self[i..] } fn trim_end_matches(&self, mut f: F) -> &[T] where F: FnMut(&T) -> bool, { let mut i = self.len(); while i > 0 && f(&self[i - 1]) { i -= 1; } &self[..i] } fn group_by_key(&self, f: F) -> GroupByKey<'_, T, F> { GroupByKey { slice: self, f } } fn split_prefix_suffix(&self, mut f: F) -> (usize, usize) where F: FnMut(&T) -> bool, { let start = self.iter().position(|v| !f(v)).unwrap_or(self.len()); let end = self .iter() .skip(start) .rposition(|v| !f(v)) .map_or(start, |i| start + i + 1); (start, end) } } /// This struct is created by [`SliceExt::group_by_key`]. pub struct GroupByKey<'a, T, F> { slice: &'a [T], f: F, } impl<'a, T, K, F> Iterator for GroupByKey<'a, T, F> where F: FnMut(&T) -> K, K: PartialEq, { type Item = (K, &'a [T]); fn next(&mut self) -> Option { let mut iter = self.slice.iter(); let key = (self.f)(iter.next()?); let count = 1 + iter.take_while(|t| (self.f)(t) == key).count(); let (head, tail) = self.slice.split_at(count); self.slice = tail; Some((key, head)) } } /// Adapter for reversing iterators conditionally. pub trait MaybeReverseIter { type RevIfIter; /// Reverse this iterator (apply .rev()) based on some condition. fn rev_if(self, condition: bool) -> Self::RevIfIter where Self: Sized; } impl MaybeReverseIter for I { type RevIfIter = Chain>, Flatten>>>; fn rev_if(self, condition: bool) -> Self::RevIfIter where Self: Sized, { let (maybe_self_iter, maybe_rev_iter) = if condition { (None, Some(self.rev())) } else { (Some(self), None) }; maybe_self_iter .into_iter() .flatten() .chain(maybe_rev_iter.into_iter().flatten()) } } /// Check if the [`Option`]-wrapped L is same to R. pub fn option_eq(left: Option, other: R) -> bool where L: PartialEq, { left.is_some_and(|v| v == other) } /// A container around a static reference that is cheap to clone and hash. #[derive(Debug)] pub struct Static(pub &'static T); impl Deref for Static { type Target = T; fn deref(&self) -> &Self::Target { self.0 } } impl Copy for Static {} impl Clone for Static { fn clone(&self) -> Self { *self } } impl Eq for Static {} impl PartialEq for Static { fn eq(&self, other: &Self) -> bool { std::ptr::eq(self.0, other.0) } } impl Hash for Static { fn hash(&self, state: &mut H) { state.write_usize(self.0 as *const _ as _); } } /// Generic access to a structure's components. pub trait Get { /// The structure's component type. type Component; /// Borrow the component for the specified index. fn get_ref(&self, index: Index) -> &Self::Component; /// Borrow the component for the specified index mutably. fn get_mut(&mut self, index: Index) -> &mut Self::Component; /// Convenience method for getting a copy of a component. fn get(self, index: Index) -> Self::Component where Self: Sized, Self::Component: Copy, { *self.get_ref(index) } /// Convenience method for setting a component. fn set(&mut self, index: Index, component: Self::Component) { *self.get_mut(index) = component; } /// Builder-style method for setting a component. fn with(mut self, index: Index, component: Self::Component) -> Self where Self: Sized, { self.set(index, component); self } } /// A numeric type. pub trait Numeric: Sized + Debug + Copy + PartialEq + Neg + Add + Sub + Mul + Div { /// The identity element for addition. fn zero() -> Self; /// Whether `self` is zero. fn is_zero(self) -> bool { self == Self::zero() } /// Whether `self` consists only of finite parts. fn is_finite(self) -> bool; } /// Returns the default math class of a character in Typst, if it has one. /// /// This is determined by the Unicode math class, with some manual overrides. pub fn default_math_class(c: char) -> Option { match c { // Better spacing. // https://github.com/typst/typst/commit/2e039cb052fcb768027053cbf02ce396f6d7a6be ':' => Some(MathClass::Relation), // Better spacing when used alongside + PLUS SIGN. // https://github.com/typst/typst/pull/1726 '⋯' | '⋱' | '⋰' | '⋮' => Some(MathClass::Normal), // Better spacing. // https://github.com/typst/typst/pull/1855 '.' | '/' => Some(MathClass::Normal), // ⊥ UP TACK should not be a relation, contrary to ⟂ PERPENDICULAR. // https://github.com/typst/typst/pull/5714 '\u{22A5}' => Some(MathClass::Normal), // Used as a binary connector in linear logic, where it is referred to // as "par". // https://github.com/typst/typst/issues/5764 '⅋' => Some(MathClass::Binary), // Those overrides should become the default in the next revision of // MathClass.txt. // https://github.com/typst/typst/issues/5764#issuecomment-2632435247 '⎰' | '⟅' => Some(MathClass::Opening), '⎱' | '⟆' => Some(MathClass::Closing), // Both ∨ and ⟑ are classified as Binary. // https://github.com/typst/typst/issues/5764 '⟇' => Some(MathClass::Binary), c => unicode_math_class::class(c), } }