diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-07-29 11:35:49 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-07-29 11:35:49 +0200 |
| commit | 312dcd070cf79c1dd5503f90ef10588fe4612108 (patch) | |
| tree | d995673742bfddbc107dc8d384e1d8ecd4d0ccb6 /src/util/mod.rs | |
| parent | 6ebe6218343a520dce2a5e560edbdc6fa0e9e44b (diff) | |
Move EcoString and OptionExt into util
Diffstat (limited to 'src/util/mod.rs')
| -rw-r--r-- | src/util/mod.rs | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 00000000..2c53bc70 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,155 @@ +//! Utilities. + +mod eco; + +pub use eco::EcoString; + +use std::cmp::Ordering; +use std::ops::Range; +use std::path::{Component, Path, PathBuf}; + +/// Additional methods for options. +pub trait OptionExt<T> { + /// Replace `self` with `other` if `self` is `Some`. + fn and_set(&mut self, other: Option<T>); + + /// Sets `other` as the value if `self` is `None` or if it contains a value + /// larger than `other`. + fn set_min(&mut self, other: T) + where + T: Ord; + + /// Sets `other` as the value if `self` is `None` or if it contains a value + /// smaller than `other`. + fn set_max(&mut self, other: T) + where + T: Ord; +} + +impl<T> OptionExt<T> for Option<T> { + fn and_set(&mut self, other: Option<T>) { + if self.is_some() { + *self = other; + } + } + + fn set_min(&mut self, other: T) + where + T: Ord, + { + if self.as_ref().map_or(true, |x| other < *x) { + *self = Some(other); + } + } + + fn set_max(&mut self, other: T) + where + T: Ord, + { + if self.as_ref().map_or(true, |x| other > *x) { + *self = Some(other); + } + } +} + +/// Additional methods for slices. +pub trait SliceExt<T> { + /// Split a slice into consecutive groups with the same key. + /// + /// Returns an iterator of pairs of a key and the group with that key. + fn group_by_key<K, F>(&self, f: F) -> GroupByKey<'_, T, F> + where + F: FnMut(&T) -> K, + K: PartialEq; +} + +impl<T> SliceExt<T> for [T] { + fn group_by_key<K, F>(&self, f: F) -> GroupByKey<'_, T, F> + where + F: FnMut(&T) -> K, + K: PartialEq, + { + GroupByKey { slice: self, f } + } +} + +/// This struct is produced 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<Self::Item> { + let first = self.slice.first()?; + let key = (self.f)(first); + + let mut i = 1; + while self.slice.get(i).map_or(false, |t| (self.f)(t) == key) { + i += 1; + } + + let (head, tail) = self.slice.split_at(i); + self.slice = tail; + Some((key, head)) + } +} + +/// Additional methods for [`Range<usize>`]. +pub trait RangeExt { + /// Locate a position relative to a range. + /// + /// This can be used for binary searching the range that contains the + /// position as follows: + /// ``` + /// # use typst::util::RangeExt; + /// assert_eq!( + /// [1..2, 2..7, 7..10].binary_search_by(|r| r.locate(5)), + /// Ok(1), + /// ); + /// ``` + fn locate(&self, pos: usize) -> Ordering; +} + +impl RangeExt for Range<usize> { + fn locate(&self, pos: usize) -> Ordering { + if pos < self.start { + Ordering::Greater + } else if pos < self.end { + Ordering::Equal + } else { + Ordering::Less + } + } +} + +/// Additional methods for [`Path`]. +pub trait PathExt { + /// Lexically normalize a path. + fn normalize(&self) -> PathBuf; +} + +impl PathExt for Path { + fn normalize(&self) -> PathBuf { + let mut out = PathBuf::new(); + for component in self.components() { + match component { + Component::CurDir => {} + Component::ParentDir => match out.components().next_back() { + Some(Component::Normal(_)) => { + out.pop(); + } + _ => out.push(component), + }, + _ => out.push(component), + } + } + out + } +} |
