summaryrefslogtreecommitdiff
path: root/src/util.rs
diff options
context:
space:
mode:
authorMartin <mhaug@live.de>2021-04-07 13:50:21 +0200
committerGitHub <noreply@github.com>2021-04-07 13:50:21 +0200
commitdf58a4d89b67783b1ffc5c3b7282302d59db8c70 (patch)
tree2bdc3a7ad1704ccee7c14972df1fa3cb9c77097a /src/util.rs
parent318eb9021edc493f5181247dbb7963de34126688 (diff)
parent3d2ee54848db80a8ede7e00fd5a53bc059138122 (diff)
Merge pull request #19 from typst/shape-runs 🔀
Text work
Diffstat (limited to 'src/util.rs')
-rw-r--r--src/util.rs81
1 files changed, 81 insertions, 0 deletions
diff --git a/src/util.rs b/src/util.rs
new file mode 100644
index 00000000..72db4518
--- /dev/null
+++ b/src/util.rs
@@ -0,0 +1,81 @@
+//! Utilities.
+
+use std::cmp::Ordering;
+use std::ops::Range;
+
+/// 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
+ }
+ }
+}