logicaffeine_data/
indexing.rs1use rustc_hash::FxHashMap;
20use std::hash::Hash;
21
22pub trait LogosIndex<I> {
41 type Output;
43 fn logos_get(&self, index: I) -> Self::Output;
45}
46
47pub trait LogosIndexMut<I>: LogosIndex<I> {
65 fn logos_set(&mut self, index: I, value: Self::Output);
67}
68
69impl<T: Clone> LogosIndex<i64> for Vec<T> {
72 type Output = T;
73
74 #[inline(always)]
75 fn logos_get(&self, index: i64) -> T {
76 if index < 1 {
77 panic!("Index {} is invalid: LOGOS uses 1-based indexing (minimum is 1)", index);
78 }
79 let idx = (index - 1) as usize;
80 if idx >= self.len() {
81 panic!("Index {} is out of bounds for seq of length {}", index, self.len());
82 }
83 unsafe { self.get_unchecked(idx).clone() }
84 }
85}
86
87impl<T: Clone> LogosIndexMut<i64> for Vec<T> {
88 #[inline(always)]
89 fn logos_set(&mut self, index: i64, value: T) {
90 if index < 1 {
91 panic!("Index {} is invalid: LOGOS uses 1-based indexing (minimum is 1)", index);
92 }
93 let idx = (index - 1) as usize;
94 if idx >= self.len() {
95 panic!("Index {} is out of bounds for seq of length {}", index, self.len());
96 }
97 unsafe { *self.get_unchecked_mut(idx) = value; }
98 }
99}
100
101impl<T: Clone> LogosIndex<i64> for [T] {
104 type Output = T;
105
106 #[inline(always)]
107 fn logos_get(&self, index: i64) -> T {
108 if index < 1 {
109 panic!("Index {} is invalid: LOGOS uses 1-based indexing (minimum is 1)", index);
110 }
111 let idx = (index - 1) as usize;
112 if idx >= self.len() {
113 panic!("Index {} is out of bounds for seq of length {}", index, self.len());
114 }
115 unsafe { self.get_unchecked(idx).clone() }
116 }
117}
118
119impl<T: Clone> LogosIndexMut<i64> for [T] {
120 #[inline(always)]
121 fn logos_set(&mut self, index: i64, value: T) {
122 if index < 1 {
123 panic!("Index {} is invalid: LOGOS uses 1-based indexing (minimum is 1)", index);
124 }
125 let idx = (index - 1) as usize;
126 if idx >= self.len() {
127 panic!("Index {} is out of bounds for seq of length {}", index, self.len());
128 }
129 unsafe { *self.get_unchecked_mut(idx) = value; }
130 }
131}
132
133impl<T: Clone> LogosIndex<i64> for &mut [T] {
140 type Output = T;
141
142 #[inline(always)]
143 fn logos_get(&self, index: i64) -> T {
144 <[T] as LogosIndex<i64>>::logos_get(self, index)
145 }
146}
147
148impl<T: Clone> LogosIndexMut<i64> for &mut [T] {
149 #[inline(always)]
150 fn logos_set(&mut self, index: i64, value: T) {
151 <[T] as LogosIndexMut<i64>>::logos_set(self, index, value)
152 }
153}
154
155impl LogosIndex<i64> for String {
158 type Output = String;
159
160 #[inline(always)]
161 fn logos_get(&self, index: i64) -> String {
162 if index < 1 {
163 panic!("Index {} is invalid: LOGOS uses 1-based indexing (minimum is 1)", index);
164 }
165 let idx = (index - 1) as usize;
166 match self.as_bytes().get(idx) {
167 Some(&b) if b.is_ascii() => {
168 String::from(b as char)
170 }
171 _ => {
172 self.chars().nth(idx)
174 .map(|c| c.to_string())
175 .unwrap_or_else(|| panic!("Index {} is out of bounds for text of length {}", index, self.chars().count()))
176 }
177 }
178 }
179}
180
181pub trait LogosGetChar {
189 fn logos_get_char(&self, index: i64) -> char;
190}
191
192impl LogosGetChar for String {
193 #[inline(always)]
194 fn logos_get_char(&self, index: i64) -> char {
195 if index < 1 {
196 panic!("Index {} is invalid: LOGOS uses 1-based indexing (minimum is 1)", index);
197 }
198 let idx = (index - 1) as usize;
199 match self.as_bytes().get(idx) {
200 Some(&b) if b.is_ascii() => b as char,
201 _ => {
202 self.chars().nth(idx)
203 .unwrap_or_else(|| panic!(
204 "Index {} is out of bounds for text of length {}",
205 index, self.chars().count()
206 ))
207 }
208 }
209 }
210}
211
212impl<K: Eq + Hash, V: Clone> LogosIndex<K> for FxHashMap<K, V> {
215 type Output = V;
216
217 #[inline(always)]
218 fn logos_get(&self, key: K) -> V {
219 self.get(&key).cloned().expect("Key not found in map")
220 }
221}
222
223impl<K: Eq + Hash, V: Clone> LogosIndexMut<K> for FxHashMap<K, V> {
224 #[inline(always)]
225 fn logos_set(&mut self, key: K, value: V) {
226 self.insert(key, value);
227 }
228}
229
230impl<V: Clone> LogosIndex<&str> for FxHashMap<String, V> {
233 type Output = V;
234
235 #[inline(always)]
236 fn logos_get(&self, key: &str) -> V {
237 self.get(key).cloned().expect("Key not found in map")
238 }
239}
240
241impl<V: Clone> LogosIndexMut<&str> for FxHashMap<String, V> {
242 #[inline(always)]
243 fn logos_set(&mut self, key: &str, value: V) {
244 self.insert(key.to_string(), value);
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
253 fn vec_1_based_indexing() {
254 let v = vec![10, 20, 30];
255 assert_eq!(LogosIndex::logos_get(&v, 1i64), 10);
256 assert_eq!(LogosIndex::logos_get(&v, 2i64), 20);
257 assert_eq!(LogosIndex::logos_get(&v, 3i64), 30);
258 }
259
260 #[test]
261 #[should_panic(expected = "1-based indexing")]
262 fn vec_zero_index_panics() {
263 let v = vec![10, 20, 30];
264 let _ = LogosIndex::logos_get(&v, 0i64);
265 }
266
267 #[test]
268 fn vec_set_1_based() {
269 let mut v = vec![10, 20, 30];
270 LogosIndexMut::logos_set(&mut v, 2i64, 99);
271 assert_eq!(v, vec![10, 99, 30]);
272 }
273
274 #[test]
275 fn hashmap_string_key() {
276 let mut m: FxHashMap<String, i64> = FxHashMap::default();
277 m.insert("iron".to_string(), 42);
278 assert_eq!(LogosIndex::logos_get(&m, "iron".to_string()), 42);
279 }
280
281 #[test]
282 fn hashmap_str_key() {
283 let mut m: FxHashMap<String, i64> = FxHashMap::default();
284 m.insert("iron".to_string(), 42);
285 assert_eq!(LogosIndex::logos_get(&m, "iron"), 42);
286 }
287
288 #[test]
289 fn hashmap_set_key() {
290 let mut m: FxHashMap<String, i64> = FxHashMap::default();
291 LogosIndexMut::logos_set(&mut m, "iron", 42i64);
292 assert_eq!(m.get("iron"), Some(&42));
293 }
294}