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
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<T: Clone> LogosIndex<i64> for crate::types::LogosSeq<T> {
215 type Output = T;
216
217 #[inline(always)]
218 fn logos_get(&self, index: i64) -> T {
219 let inner = self.borrow();
220 <Vec<T> as LogosIndex<i64>>::logos_get(&*inner, index)
221 }
222}
223
224impl<T: Clone> LogosIndexMut<i64> for crate::types::LogosSeq<T> {
225 #[inline(always)]
226 fn logos_set(&mut self, index: i64, value: T) {
227 let mut inner = self.borrow_mut();
228 <Vec<T> as LogosIndexMut<i64>>::logos_set(&mut *inner, index, value)
229 }
230}
231
232impl<K: Eq + Hash, V: Clone> LogosIndex<K> for crate::types::LogosMap<K, V> {
235 type Output = V;
236
237 #[inline(always)]
238 fn logos_get(&self, key: K) -> V {
239 let inner = self.borrow();
240 inner.get(&key).cloned().expect("Key not found in map")
241 }
242}
243
244impl<K: Eq + Hash, V: Clone> LogosIndexMut<K> for crate::types::LogosMap<K, V> {
245 #[inline(always)]
246 fn logos_set(&mut self, key: K, value: V) {
247 self.insert(key, value);
248 }
249}
250
251impl<V: Clone> LogosIndex<&str> for crate::types::LogosMap<String, V> {
254 type Output = V;
255
256 #[inline(always)]
257 fn logos_get(&self, key: &str) -> V {
258 let inner = self.borrow();
259 inner.get(key).cloned().expect("Key not found in map")
260 }
261}
262
263impl<V: Clone> LogosIndexMut<&str> for crate::types::LogosMap<String, V> {
264 #[inline(always)]
265 fn logos_set(&mut self, key: &str, value: V) {
266 self.insert(key.to_string(), value);
267 }
268}
269
270impl<K: Eq + Hash, V: Clone> LogosIndex<K> for FxHashMap<K, V> {
273 type Output = V;
274
275 #[inline(always)]
276 fn logos_get(&self, key: K) -> V {
277 self.get(&key).cloned().expect("Key not found in map")
278 }
279}
280
281impl<K: Eq + Hash, V: Clone> LogosIndexMut<K> for FxHashMap<K, V> {
282 #[inline(always)]
283 fn logos_set(&mut self, key: K, value: V) {
284 self.insert(key, value);
285 }
286}
287
288impl<V: Clone> LogosIndex<&str> for FxHashMap<String, V> {
291 type Output = V;
292
293 #[inline(always)]
294 fn logos_get(&self, key: &str) -> V {
295 self.get(key).cloned().expect("Key not found in map")
296 }
297}
298
299impl<V: Clone> LogosIndexMut<&str> for FxHashMap<String, V> {
300 #[inline(always)]
301 fn logos_set(&mut self, key: &str, value: V) {
302 self.insert(key.to_string(), value);
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309
310 #[test]
311 fn vec_1_based_indexing() {
312 let v = vec![10, 20, 30];
313 assert_eq!(LogosIndex::logos_get(&v, 1i64), 10);
314 assert_eq!(LogosIndex::logos_get(&v, 2i64), 20);
315 assert_eq!(LogosIndex::logos_get(&v, 3i64), 30);
316 }
317
318 #[test]
319 #[should_panic(expected = "1-based indexing")]
320 fn vec_zero_index_panics() {
321 let v = vec![10, 20, 30];
322 let _ = LogosIndex::logos_get(&v, 0i64);
323 }
324
325 #[test]
326 fn vec_set_1_based() {
327 let mut v = vec![10, 20, 30];
328 LogosIndexMut::logos_set(&mut v, 2i64, 99);
329 assert_eq!(v, vec![10, 99, 30]);
330 }
331
332 #[test]
333 fn hashmap_string_key() {
334 let mut m: FxHashMap<String, i64> = FxHashMap::default();
335 m.insert("iron".to_string(), 42);
336 assert_eq!(LogosIndex::logos_get(&m, "iron".to_string()), 42);
337 }
338
339 #[test]
340 fn hashmap_str_key() {
341 let mut m: FxHashMap<String, i64> = FxHashMap::default();
342 m.insert("iron".to_string(), 42);
343 assert_eq!(LogosIndex::logos_get(&m, "iron"), 42);
344 }
345
346 #[test]
347 fn hashmap_set_key() {
348 let mut m: FxHashMap<String, i64> = FxHashMap::default();
349 LogosIndexMut::logos_set(&mut m, "iron", 42i64);
350 assert_eq!(m.get("iron"), Some(&42));
351 }
352}