Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/linear_index.rs
StratoSDK / crates / strato-ui-core / src / linear_index.rs
1use std::marker::PhantomData;
2use std::ops::AddAssign;
3 
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum SeekBias {
6 Left,
7 Right,
8}
9 
10pub trait Item {
11 type Summary: Clone + Default;
12 
13 fn summary(&self) -> Self::Summary;
14}
15 
16pub trait Dimension<'a, Summary> {
17 fn add_summary(&mut self, summary: &'a Summary);
18}
19 
20impl<'a, Summary> Dimension<'a, Summary> for () {
21 fn add_summary(&mut self, _summary: &'a Summary) {}
22}
23 
24#[derive(Clone, Debug)]
25pub struct SumTree<T: Item + Clone> {
26 items: Vec<T>,
27}
28 
29impl<T: Item + Clone> SumTree<T> {
30 pub fn new() -> Self {
31 Self { items: Vec::new() }
32 }
33 
34 pub fn push(&mut self, item: T) {
35 self.items.push(item);
36 }
37 
38 pub fn push_tree(&mut self, mut tree: Self) {
39 self.items.append(&mut tree.items);
40 }
41 
42 pub fn extend<I>(&mut self, items: I)
43 where
44 I: IntoIterator<Item = T>,
45 {
46 self.items.extend(items);
47 }
48 
49 pub fn summary(&self) -> T::Summary
50 where
51 for<'a> T::Summary: AddAssign<&'a T::Summary>,
52 {
53 summarize::<T>(&self.items)
54 }
55 
56 pub fn cursor<SeekDimension, StartDimension>(&self) -> Cursor<T, SeekDimension, StartDimension>
57 where
58 StartDimension: Default,
59 {
60 Cursor {
61 items: self.items.clone(),
62 position: 0,
63 start: StartDimension::default(),
64 _seek_dimension: PhantomData,
65 }
66 }
67}
68 
69impl<T: Item + Clone> Default for SumTree<T> {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74 
75pub struct Cursor<T, SeekDimension, StartDimension>
76where
77 T: Item + Clone,
78{
79 items: Vec<T>,
80 position: usize,
81 start: StartDimension,
82 _seek_dimension: PhantomData<SeekDimension>,
83}
84 
85impl<T, SeekDimension, StartDimension> Cursor<T, SeekDimension, StartDimension>
86where
87 T: Item + Clone,
88 StartDimension: Clone + Default,
89 for<'a> StartDimension: Dimension<'a, T::Summary>,
90{
91 pub fn seek(&mut self, target: &SeekDimension, bias: SeekBias)
92 where
93 SeekDimension: Clone + Default + PartialOrd,
94 for<'a> SeekDimension: Dimension<'a, T::Summary>,
95 {
96 let (position, start) =
97 seek_position::<T, SeekDimension, StartDimension>(&self.items, target, bias);
98 self.position = position;
99 self.start = start;
100 }
101 
102 pub fn start(&self) -> &StartDimension {
103 &self.start
104 }
105 
106 pub fn item(&self) -> Option<&T> {
107 self.items.get(self.position)
108 }
109 
110 pub fn next(&mut self) {
111 if let Some(item) = self.items.get(self.position) {
112 self.start.add_summary(&item.summary());
113 self.position += 1;
114 }
115 }
116 
117 pub fn slice(&mut self, target: &SeekDimension, bias: SeekBias) -> SumTree<T>
118 where
119 SeekDimension: Clone + Default + PartialOrd,
120 for<'a> SeekDimension: Dimension<'a, T::Summary>,
121 {
122 self.seek(target, bias);
123 SumTree {
124 items: self.items[..self.position].to_vec(),
125 }
126 }
127 
128 pub fn suffix(&self) -> SumTree<T> {
129 SumTree {
130 items: self.items[self.position..].to_vec(),
131 }
132 }
133 
134 pub fn enumerate(&self) -> impl Iterator<Item = (usize, T)> + '_ {
135 self.items[self.position..].iter().cloned().enumerate()
136 }
137}
138 
139fn summarize<T>(items: &[T]) -> T::Summary
140where
141 T: Item + Clone,
142 for<'a> T::Summary: AddAssign<&'a T::Summary>,
143{
144 let mut summary = T::Summary::default();
145 for item in items {
146 summary += &item.summary();
147 }
148 summary
149}
150 
151fn seek_position<T, SeekDimension, StartDimension>(
152 items: &[T],
153 target: &SeekDimension,
154 bias: SeekBias,
155) -> (usize, StartDimension)
156where
157 T: Item + Clone,
158 SeekDimension: Clone + Default + PartialOrd,
159 StartDimension: Clone + Default,
160 for<'a> SeekDimension: Dimension<'a, T::Summary>,
161 for<'a> StartDimension: Dimension<'a, T::Summary>,
162{
163 let mut seek_dimension = SeekDimension::default();
164 let mut start_dimension = StartDimension::default();
165 
166 for (index, item) in items.iter().enumerate() {
167 let summary = item.summary();
168 let mut next_seek_dimension = seek_dimension.clone();
169 next_seek_dimension.add_summary(&summary);
170 
171 let found = match bias {
172 SeekBias::Left => next_seek_dimension >= *target,
173 SeekBias::Right => next_seek_dimension > *target,
174 };
175 
176 if found {
177 return (index, start_dimension);
178 }
179 
180 seek_dimension = next_seek_dimension;
181 start_dimension.add_summary(&summary);
182 }
183 
184 (items.len(), start_dimension)
185}
186