1use 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
69#[inline(always)]
73pub fn resolve_logos_index(index: i64, len: usize) -> usize {
74 if index >= 1 {
75 let idx = (index - 1) as usize;
76 if idx >= len {
77 panic!("Index {} is out of bounds for seq of length {}", index, len);
78 }
79 idx
80 } else if index <= -1 {
81 let back = index.unsigned_abs() as usize;
82 if back > len {
83 panic!("Index {} is out of bounds for seq of length {}", index, len);
84 }
85 len - back
86 } else {
87 panic!("Index 0 is invalid: LOGOS uses 1-based indexing (minimum is 1, and -1 reads from the end)");
88 }
89}
90
91impl<T: Clone> LogosIndex<i64> for Vec<T> {
94 type Output = T;
95
96 #[inline(always)]
97 fn logos_get(&self, index: i64) -> T {
98 let idx = resolve_logos_index(index, self.len());
99 unsafe { self.get_unchecked(idx).clone() }
100 }
101}
102
103impl<T: Clone> LogosIndexMut<i64> for Vec<T> {
104 #[inline(always)]
105 fn logos_set(&mut self, index: i64, value: T) {
106 let idx = resolve_logos_index(index, self.len());
107 unsafe { *self.get_unchecked_mut(idx) = value; }
108 }
109}
110
111impl<T: Clone> LogosIndex<i64> for [T] {
114 type Output = T;
115
116 #[inline(always)]
117 fn logos_get(&self, index: i64) -> T {
118 let idx = resolve_logos_index(index, self.len());
119 unsafe { self.get_unchecked(idx).clone() }
120 }
121}
122
123impl<T: Clone> LogosIndexMut<i64> for [T] {
124 #[inline(always)]
125 fn logos_set(&mut self, index: i64, value: T) {
126 let idx = resolve_logos_index(index, self.len());
127 unsafe { *self.get_unchecked_mut(idx) = value; }
128 }
129}
130
131impl<T: Clone> LogosIndex<i64> for &mut [T] {
138 type Output = T;
139
140 #[inline(always)]
141 fn logos_get(&self, index: i64) -> T {
142 <[T] as LogosIndex<i64>>::logos_get(self, index)
143 }
144}
145
146impl<T: Clone> LogosIndexMut<i64> for &mut [T] {
147 #[inline(always)]
148 fn logos_set(&mut self, index: i64, value: T) {
149 <[T] as LogosIndexMut<i64>>::logos_set(self, index, value)
150 }
151}
152
153impl LogosIndex<i64> for String {
156 type Output = String;
157
158 #[inline(always)]
159 fn logos_get(&self, index: i64) -> String {
160 let idx = if index >= 1 {
163 (index - 1) as usize
164 } else {
165 resolve_logos_index(index, self.chars().count())
166 };
167 match self.as_bytes().get(idx) {
168 Some(&b) if b.is_ascii() => {
169 String::from(b as char)
171 }
172 _ => {
173 self.chars().nth(idx)
175 .map(|c| c.to_string())
176 .unwrap_or_else(|| panic!("Index {} is out of bounds for text of length {}", index, self.chars().count()))
177 }
178 }
179 }
180}
181
182pub trait LogosGetChar {
190 fn logos_get_char(&self, index: i64) -> char;
191}
192
193impl LogosGetChar for String {
194 #[inline(always)]
195 fn logos_get_char(&self, index: i64) -> char {
196 let idx = if index >= 1 {
197 (index - 1) as usize
198 } else {
199 resolve_logos_index(index, self.chars().count())
200 };
201 match self.as_bytes().get(idx) {
202 Some(&b) if b.is_ascii() => b as char,
203 _ => {
204 self.chars().nth(idx)
205 .unwrap_or_else(|| panic!(
206 "Index {} is out of bounds for text of length {}",
207 index, self.chars().count()
208 ))
209 }
210 }
211 }
212}
213
214impl<T: Clone> LogosIndex<i64> for crate::types::LogosSeq<T> {
217 type Output = T;
218
219 #[inline(always)]
220 fn logos_get(&self, index: i64) -> T {
221 let inner = self.borrow();
222 <Vec<T> as LogosIndex<i64>>::logos_get(&*inner, index)
223 }
224}
225
226impl<T: Clone> LogosIndexMut<i64> for crate::types::LogosSeq<T> {
227 #[inline(always)]
228 fn logos_set(&mut self, index: i64, value: T) {
229 let mut inner = self.borrow_mut();
230 <Vec<T> as LogosIndexMut<i64>>::logos_set(&mut *inner, index, value)
231 }
232}
233
234impl<K: Eq + Hash, V: Clone> LogosIndex<K> for crate::types::LogosMap<K, V> {
237 type Output = V;
238
239 #[inline(always)]
240 fn logos_get(&self, key: K) -> V {
241 let inner = self.borrow();
242 inner.get(&key).cloned().expect("Key not found in map")
243 }
244}
245
246impl<K: Eq + Hash, V: Clone> LogosIndexMut<K> for crate::types::LogosMap<K, V> {
247 #[inline(always)]
248 fn logos_set(&mut self, key: K, value: V) {
249 self.insert(key, value);
250 }
251}
252
253impl<V: Clone> LogosIndex<&str> for crate::types::LogosMap<String, V> {
256 type Output = V;
257
258 #[inline(always)]
259 fn logos_get(&self, key: &str) -> V {
260 let inner = self.borrow();
261 inner.get(key).cloned().expect("Key not found in map")
262 }
263}
264
265impl<V: Clone> LogosIndexMut<&str> for crate::types::LogosMap<String, V> {
266 #[inline(always)]
267 fn logos_set(&mut self, key: &str, value: V) {
268 self.insert(key.to_string(), value);
269 }
270}
271
272impl<K: Eq + Hash, V: Clone> LogosIndex<K> for FxHashMap<K, V> {
275 type Output = V;
276
277 #[inline(always)]
278 fn logos_get(&self, key: K) -> V {
279 self.get(&key).cloned().expect("Key not found in map")
280 }
281}
282
283impl<K: Eq + Hash, V: Clone> LogosIndexMut<K> for FxHashMap<K, V> {
284 #[inline(always)]
285 fn logos_set(&mut self, key: K, value: V) {
286 self.insert(key, value);
287 }
288}
289
290impl<V: Clone> LogosIndex<&str> for FxHashMap<String, V> {
293 type Output = V;
294
295 #[inline(always)]
296 fn logos_get(&self, key: &str) -> V {
297 self.get(key).cloned().expect("Key not found in map")
298 }
299}
300
301impl<V: Clone> LogosIndexMut<&str> for FxHashMap<String, V> {
302 #[inline(always)]
303 fn logos_set(&mut self, key: &str, value: V) {
304 self.insert(key.to_string(), value);
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311
312 #[test]
313 fn vec_1_based_indexing() {
314 let v = vec![10, 20, 30];
315 assert_eq!(LogosIndex::logos_get(&v, 1i64), 10);
316 assert_eq!(LogosIndex::logos_get(&v, 2i64), 20);
317 assert_eq!(LogosIndex::logos_get(&v, 3i64), 30);
318 }
319
320 #[test]
321 #[should_panic(expected = "1-based indexing")]
322 fn vec_zero_index_panics() {
323 let v = vec![10, 20, 30];
324 let _ = LogosIndex::logos_get(&v, 0i64);
325 }
326
327 #[test]
328 fn vec_set_1_based() {
329 let mut v = vec![10, 20, 30];
330 LogosIndexMut::logos_set(&mut v, 2i64, 99);
331 assert_eq!(v, vec![10, 99, 30]);
332 }
333
334 #[test]
335 fn hashmap_string_key() {
336 let mut m: FxHashMap<String, i64> = FxHashMap::default();
337 m.insert("iron".to_string(), 42);
338 assert_eq!(LogosIndex::logos_get(&m, "iron".to_string()), 42);
339 }
340
341 #[test]
342 fn hashmap_str_key() {
343 let mut m: FxHashMap<String, i64> = FxHashMap::default();
344 m.insert("iron".to_string(), 42);
345 assert_eq!(LogosIndex::logos_get(&m, "iron"), 42);
346 }
347
348 #[test]
349 fn hashmap_set_key() {
350 let mut m: FxHashMap<String, i64> = FxHashMap::default();
351 LogosIndexMut::logos_set(&mut m, "iron", 42i64);
352 assert_eq!(m.get("iron"), Some(&42));
353 }
354}