Skip to main content

scratch_test_value/
lists.rs

1use itertools::Itertools;
2
3use crate::{
4    QuirkSink, SValue,
5    quirks::{NumberTooBigForIntQ, SValueToNumberQ},
6};
7
8#[derive(Debug, PartialEq, Clone)]
9#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
10#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
11pub struct SList {
12    data: Vec<SValue>,
13    max_length: i64,
14}
15
16#[allow(clippy::enum_variant_names)]
17pub enum NthItemOfListQ {
18    PositionIsNotInt(SValue),
19    PositionZero,
20    PositionNegative(i64),
21    PositionTooBig(i64),
22}
23
24#[derive(Debug, thiserror::Error)]
25#[error("List already reached maximum length, element insertion failed")]
26pub struct SListAlreadyFull(());
27
28pub enum Indexing {
29    /// Zero or below or more than one more than the current length
30    Invalid,
31    /// Valid zero-based index in the current state of the list data
32    Normal(usize),
33    /// Exactly one after the last element, important for insertions
34    OneMore,
35}
36
37impl IntoIterator for SList {
38    type Item = SValue;
39    type IntoIter = std::vec::IntoIter<SValue>;
40    fn into_iter(self) -> Self::IntoIter {
41        self.data.into_iter()
42    }
43}
44
45/// Be careful that this will set the maximum length to
46/// the iterators length. If further insertions should be possible,
47/// reset the max length using [`Self::set_max_length`] afterwards.
48impl<C: Into<SValue>> FromIterator<C> for SList {
49    fn from_iter<T: IntoIterator<Item = C>>(iter: T) -> Self {
50        let v = iter.into_iter().map_into().collect_vec();
51        Self {
52            max_length: v.len() as i64,
53            data: v,
54        }
55    }
56}
57
58#[derive(Debug, thiserror::Error)]
59#[error("The maximum length can't be smaller than the number of items in the list")]
60pub struct ListLongerThanNewMaximum;
61
62impl SList {
63    pub fn new(max_length: i64) -> Self {
64        Self {
65            data: vec![],
66            max_length,
67        }
68    }
69    pub fn iter(&self) -> impl DoubleEndedIterator<Item = &SValue> {
70        self.data().iter()
71    }
72
73    pub fn delete_all(&mut self) {
74        self.data.clear();
75    }
76    pub fn length(&self) -> i64 {
77        self.data.len() as i64
78    }
79    pub fn textual_representation(&self) -> String {
80        self.data.iter().join(" ")
81    }
82    pub fn contains_item(&self, item: &SValue) -> bool {
83        self.data.iter().any(|x| x.q_eq(item, &mut ()))
84    }
85
86    fn index_for_value<Q>(&self, one_based_n: &SValue, sink: &mut Q, is_insert: bool) -> Indexing
87    where
88        Q: QuirkSink<NthItemOfListQ> + QuirkSink<SValueToNumberQ> + QuirkSink<NumberTooBigForIntQ>,
89    {
90        let index = one_based_n.floor(sink).int_or_border(sink);
91
92        if !one_based_n.is_int() {
93            sink.put(NthItemOfListQ::PositionIsNotInt(one_based_n.clone()));
94            Indexing::Invalid
95        } else if index < 0 {
96            sink.put(NthItemOfListQ::PositionNegative(index));
97            Indexing::Invalid
98        } else if index == 0 {
99            sink.put(NthItemOfListQ::PositionZero);
100            Indexing::Invalid
101        } else if index.try_into().unwrap_or(usize::MAX) <= self.data.len() {
102            let zero_based = (index - 1) as usize;
103            Indexing::Normal(zero_based)
104        } else if (index - 1).try_into().unwrap_or(usize::MAX) == self.data.len() {
105            if !is_insert {
106                sink.put(NthItemOfListQ::PositionTooBig(index));
107            }
108            Indexing::OneMore
109        } else {
110            sink.put(NthItemOfListQ::PositionTooBig(index));
111            Indexing::Invalid
112        }
113    }
114    /// Deletes the item at the position `one_based_n` if
115    /// `one_based_n` can be converted into an integer in
116    /// range `[1; self.length()]`.
117    ///
118    /// Returns the removed item if it was removed:
119    /// - `Some(item)`: `one_based_n` referred to a valid position, item removed
120    /// - `None`: `one_based_n` was invalid (0, too big, ...), nothing changed
121    pub fn delete_nth<Q>(&mut self, one_based_n: &SValue, sink: &mut Q) -> Option<SValue>
122    where
123        Q: QuirkSink<NthItemOfListQ> + QuirkSink<SValueToNumberQ> + QuirkSink<NumberTooBigForIntQ>,
124    {
125        match self.index_for_value(one_based_n, sink, false) {
126            Indexing::Normal(zero_based) => Some(self.data.remove(zero_based)),
127            Indexing::Invalid | Indexing::OneMore => None,
128        }
129    }
130
131    /// Appends an item to the end of the list
132    pub fn append_item(&mut self, item: SValue) -> Result<(), SListAlreadyFull> {
133        if self.length() < self.max_length {
134            self.data.push(item);
135            Ok(())
136        } else {
137            Err(SListAlreadyFull(()))
138        }
139    }
140
141    /// Inserts the `item` at the position `one_based_n` if
142    /// `one_based_n` can be converted into an integer in
143    /// range `[1; self.length() + 1]`.
144    ///
145    /// One more than length will be identical to appending.
146    ///
147    /// Returns if the list was changed (`bool`).
148    /// - `true`: `one_based_n` referred to a valid position, item inserted
149    /// - `false`: `one_based_n` was invalid (0, too big, ...), nothing changed
150    pub fn insert_item_at<Q>(
151        &mut self,
152        one_based_n: &SValue,
153        item: SValue,
154        sink: &mut Q,
155    ) -> Result<bool, SListAlreadyFull>
156    where
157        Q: QuirkSink<NthItemOfListQ> + QuirkSink<SValueToNumberQ> + QuirkSink<NumberTooBigForIntQ>,
158    {
159        if self.length() >= self.max_length {
160            return Err(SListAlreadyFull(()));
161        }
162        match self.index_for_value(one_based_n, sink, true) {
163            Indexing::Normal(i) => {
164                self.data.insert(i, item);
165                Ok(true)
166            }
167            Indexing::OneMore => {
168                self.data.push(item);
169                Ok(true)
170            }
171            Indexing::Invalid => Ok(false),
172        }
173    }
174
175    /// Replaces the n-th item with `new_val` if `one_based_n`
176    /// can be converted into an integer in range `[1; self.length()]`
177    pub fn replace_nth_item<Q>(
178        &mut self,
179        one_based_n: &SValue,
180        new_val: SValue,
181        sink: &mut Q,
182    ) -> bool
183    where
184        Q: QuirkSink<NthItemOfListQ> + QuirkSink<SValueToNumberQ> + QuirkSink<NumberTooBigForIntQ>,
185    {
186        match self.index_for_value(one_based_n, sink, false) {
187            Indexing::Invalid | Indexing::OneMore => false,
188            Indexing::Normal(i) => {
189                self.data[i] = new_val;
190                true
191            }
192        }
193    }
194
195    pub fn nth_item<Q>(&self, one_based_n: &SValue, sink: &mut Q) -> SValue
196    where
197        Q: QuirkSink<NthItemOfListQ> + QuirkSink<SValueToNumberQ> + QuirkSink<NumberTooBigForIntQ>,
198    {
199        // TODO change to borrowing when "" can be created in const context
200        match self.index_for_value(one_based_n, sink, false) {
201            Indexing::Normal(i) => self.data[i].clone(),
202            Indexing::OneMore | Indexing::Invalid => SValue::Text("".into()),
203        }
204    }
205
206    pub fn first_index_of_item_in_list(&self, item: &SValue) -> i64 {
207        self.data
208            .iter()
209            .find_position(|x| x.q_eq(item, &mut ()))
210            .map(|(pos, _)| pos as i64)
211            .unwrap_or(0)
212    }
213
214    pub fn set_max_length(&mut self, mut max: i64) -> Result<(), ListLongerThanNewMaximum> {
215        max = max.max(0);
216        if max as usize > self.data.len() {
217            Err(ListLongerThanNewMaximum)
218        } else {
219            self.max_length = max;
220            Ok(())
221        }
222    }
223
224    pub fn max_length(&self) -> i64 {
225        self.max_length
226    }
227    pub fn data(&self) -> &[SValue] {
228        &self.data
229    }
230
231    pub fn make_readonly(self) -> ReadonlySList {
232        ReadonlySList {
233            data: self.data.into(),
234            max_length: self.max_length,
235        }
236    }
237}
238
239#[derive(Debug, PartialEq, PartialOrd)]
240pub struct ReadonlySList {
241    data: crate::ARc<[SValue]>,
242    max_length: i64,
243}
244impl ReadonlySList {
245    pub fn owned_data(&self) -> crate::ARc<[SValue]> {
246        self.data.clone()
247    }
248    pub fn data(&self) -> &crate::ARc<[SValue]> {
249        &self.data
250    }
251    pub fn max_length(&self) -> i64 {
252        self.max_length
253    }
254}