Skip to main content

indigo_core/
attributes.rs

1use indigo_kernel::edit::{self, Edit};
2use roaring::RoaringBitmap;
3use std::{
4    borrow::Borrow,
5    collections::BTreeMap,
6    iter,
7    ops::{RangeBounds, RangeInclusive},
8};
9
10pub struct Attributes<A> {
11    pub inner: BTreeMap<A, RoaringBitmap>,
12}
13
14impl<A> Attributes<A> {
15    #[must_use]
16    pub fn new() -> Self {
17        Self::default()
18    }
19
20    pub fn insert<R>(&mut self, range: R, attribute: A)
21    where
22        R: RangeBounds<u32>,
23        A: Ord,
24    {
25        self.inner.entry(attribute).or_default().insert_range(range);
26    }
27
28    pub fn remove<R, Q>(&mut self, range: R, attribute: &Q)
29    where
30        R: RangeBounds<u32>,
31        A: Borrow<Q> + Ord,
32        Q: Ord + ?Sized,
33    {
34        let attribute = attribute.borrow();
35        if let Some(ranges) = self.inner.get_mut(attribute) {
36            ranges.remove_range(range);
37            if ranges.is_empty() {
38                self.inner.remove(attribute);
39            }
40        }
41    }
42
43    #[must_use]
44    pub fn contains<R, Q>(&self, range: R, attribute: &Q) -> bool
45    where
46        R: RangeBounds<u32>,
47        A: Borrow<Q> + Ord,
48        Q: Ord + ?Sized,
49    {
50        match self.inner.get(attribute.borrow()) {
51            Some(ranges) => ranges.contains_range(range),
52            None => false,
53        }
54    }
55
56    #[must_use]
57    pub fn ranges<Q>(&self, attribute: &Q) -> Option<impl Iterator<Item = RangeInclusive<u32>>>
58    where
59        A: Borrow<Q> + Ord,
60        Q: Ord + ?Sized,
61    {
62        self.inner.get(attribute.borrow()).map(|ranges| Ranges {
63            iter: ranges.iter(),
64        })
65    }
66
67    pub fn attrs<R>(&self, range: R) -> impl Iterator<Item = &A>
68    where
69        R: RangeBounds<u32> + Clone,
70    {
71        iter::zip(self.inner.iter(), iter::repeat(range))
72            .filter_map(|((attr, ranges), range)| ranges.contains_range(range).then_some(attr))
73    }
74
75    pub fn transform(&mut self, ops: &Edit) {
76        for old_ranges in self.inner.values_mut() {
77            let mut iter = old_ranges.iter();
78            let mut offsets: Vec<usize> = iter::from_fn(|| iter.next_range())
79                .flat_map(|range| {
80                    let start =
81                        usize::try_from(*range.start()).expect("Machine has 64-bit pointers");
82                    let end = usize::try_from(*range.end()).expect("Machine has 64-bit pointers");
83                    [start, end]
84                })
85                .collect();
86            ops.map_positions_sorted(&mut offsets, edit::Bias::Forward);
87            let mut new_ranges = RoaringBitmap::new();
88            let (chunks, remainder) = offsets.as_chunks::<2>();
89            for pair in chunks {
90                let start = u32::try_from(pair[0]).expect("Byte offset does not exceed u32::MAX");
91                let end = u32::try_from(pair[1]).expect("Byte offset does not exceed u32::MAX");
92                new_ranges.insert_range(start..=end);
93            }
94            assert_eq!(remainder, []);
95            *old_ranges = new_ranges;
96        }
97    }
98}
99
100impl<A> Default for Attributes<A> {
101    fn default() -> Self {
102        Self {
103            inner: BTreeMap::default(),
104        }
105    }
106}
107
108/// An iterator over ranges with a certain attribute.
109///
110/// This struct is created with the [`ranges`](Attributes::ranges) method on [`Attributes`].
111pub struct Ranges<'a> {
112    iter: roaring::bitmap::Iter<'a>,
113}
114
115impl Iterator for Ranges<'_> {
116    type Item = RangeInclusive<u32>;
117    fn next(&mut self) -> Option<Self::Item> {
118        self.iter.next_range()
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn demo() {
128        let mut attrs = Attributes::new();
129        attrs.insert(0..=4, String::from("foo"));
130        attrs.insert(2..=8, String::from("foo"));
131        assert!(attrs.contains(0..=8, "foo"));
132        attrs.remove(4..=6, "foo");
133        assert!(attrs.contains(0..=3, "foo"));
134        assert!(!attrs.contains(4..=6, "foo"));
135        assert!(attrs.contains(7..=8, "foo"));
136        let expected_ranges = vec![0..=3, 7..=8];
137        let actual_ranges: Vec<_> = attrs.ranges("foo").unwrap().collect();
138        assert_eq!(expected_ranges, actual_ranges);
139        let expected_attrs = vec!["foo"];
140        let actual_attrs: Vec<_> = attrs.attrs(0..=3).cloned().collect();
141        assert_eq!(expected_attrs, actual_attrs);
142    }
143
144    #[test]
145    fn inner() {
146        let mut attrs = Attributes::new();
147        attrs.insert(0..=10, String::from("color=red"));
148        attrs.insert(5..=15, String::from("color=green"));
149        attrs.insert(10..=20, String::from("color=blue"));
150        attrs.insert(0..=5, String::from("font=bold"));
151        attrs.insert(15..=25, String::from("font=italic"));
152
153        // Get all colors
154        let color_attrs: Vec<_> = attrs
155            .inner
156            .range(String::from("color=")..String::from("color=\u{10ffff}"))
157            .map(|(attr, _)| attr.as_str())
158            .collect();
159        assert_eq!(color_attrs, vec!["color=blue", "color=green", "color=red"]);
160
161        // Get specific colors
162        let specific_colors: Vec<_> = attrs
163            .inner
164            .range(String::from("color=blue")..=String::from("color=green"))
165            .map(|(attr, _)| attr.as_str())
166            .collect();
167        assert_eq!(specific_colors, vec!["color=blue", "color=green"]);
168
169        // Get all fonts
170        let font_attrs: Vec<_> = attrs
171            .inner
172            .range(String::from("font=")..String::from("font=\u{10ffff}"))
173            .map(|(attr, _)| attr.as_str())
174            .collect();
175        assert_eq!(font_attrs, vec!["font=bold", "font=italic"]);
176
177        // Remove colors
178        attrs.inner.retain(|attr, _| !attr.starts_with("color="));
179
180        // Only fonts remain
181        let remaining_attrs: Vec<_> = attrs.inner.keys().collect();
182        assert_eq!(remaining_attrs, vec!["font=bold", "font=italic"]);
183    }
184}