Skip to main content

oxihuman_core/
linked_map.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Linked map: insertion-ordered key→value map using a Vec + HashMap.
6
7use std::collections::HashMap;
8
9/// Insertion-ordered map.
10#[derive(Debug, Clone)]
11#[allow(dead_code)]
12pub struct LinkedMap<V> {
13    keys: Vec<String>,
14    map: HashMap<String, V>,
15}
16
17/// Create a new LinkedMap.
18#[allow(dead_code)]
19pub fn new_linked_map<V>() -> LinkedMap<V> {
20    LinkedMap {
21        keys: Vec::new(),
22        map: HashMap::new(),
23    }
24}
25
26/// Insert or update a key; preserves insertion order for new keys.
27#[allow(dead_code)]
28pub fn lmap_insert<V>(lm: &mut LinkedMap<V>, key: &str, val: V) {
29    if !lm.map.contains_key(key) {
30        lm.keys.push(key.to_string());
31    }
32    lm.map.insert(key.to_string(), val);
33}
34
35/// Get a reference by key.
36#[allow(dead_code)]
37pub fn lmap_get<'a, V>(lm: &'a LinkedMap<V>, key: &str) -> Option<&'a V> {
38    lm.map.get(key)
39}
40
41/// Get a mutable reference by key.
42#[allow(dead_code)]
43pub fn lmap_get_mut<'a, V>(lm: &'a mut LinkedMap<V>, key: &str) -> Option<&'a mut V> {
44    lm.map.get_mut(key)
45}
46
47/// Remove a key; returns value if present.
48#[allow(dead_code)]
49pub fn lmap_remove<V>(lm: &mut LinkedMap<V>, key: &str) -> Option<V> {
50    if let Some(val) = lm.map.remove(key) {
51        lm.keys.retain(|k| k != key);
52        Some(val)
53    } else {
54        None
55    }
56}
57
58/// Whether key is present.
59#[allow(dead_code)]
60pub fn lmap_contains<V>(lm: &LinkedMap<V>, key: &str) -> bool {
61    lm.map.contains_key(key)
62}
63
64/// Number of entries.
65#[allow(dead_code)]
66pub fn lmap_len<V>(lm: &LinkedMap<V>) -> usize {
67    lm.map.len()
68}
69
70/// Whether empty.
71#[allow(dead_code)]
72pub fn lmap_is_empty<V>(lm: &LinkedMap<V>) -> bool {
73    lm.map.is_empty()
74}
75
76/// Ordered keys.
77#[allow(dead_code)]
78pub fn lmap_keys<V>(lm: &LinkedMap<V>) -> &[String] {
79    &lm.keys
80}
81
82/// Ordered values.
83#[allow(dead_code)]
84pub fn lmap_values<V>(lm: &LinkedMap<V>) -> Vec<&V> {
85    lm.keys.iter().filter_map(|k| lm.map.get(k)).collect()
86}
87
88/// Clear all entries.
89#[allow(dead_code)]
90pub fn lmap_clear<V>(lm: &mut LinkedMap<V>) {
91    lm.keys.clear();
92    lm.map.clear();
93}
94
95/// Get entry by insertion position.
96#[allow(dead_code)]
97pub fn lmap_get_at<V>(lm: &LinkedMap<V>, pos: usize) -> Option<(&str, &V)> {
98    lm.keys
99        .get(pos)
100        .and_then(|k| lm.map.get(k).map(|v| (k.as_str(), v)))
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_insert_get() {
109        let mut lm: LinkedMap<u32> = new_linked_map();
110        lmap_insert(&mut lm, "a", 1);
111        assert_eq!(lmap_get(&lm, "a"), Some(&1));
112    }
113
114    #[test]
115    fn test_order_preserved() {
116        let mut lm: LinkedMap<u32> = new_linked_map();
117        lmap_insert(&mut lm, "c", 3);
118        lmap_insert(&mut lm, "a", 1);
119        lmap_insert(&mut lm, "b", 2);
120        assert_eq!(lmap_keys(&lm), &["c", "a", "b"]);
121    }
122
123    #[test]
124    fn test_update_no_reorder() {
125        let mut lm: LinkedMap<u32> = new_linked_map();
126        lmap_insert(&mut lm, "x", 1);
127        lmap_insert(&mut lm, "y", 2);
128        lmap_insert(&mut lm, "x", 99);
129        assert_eq!(lmap_keys(&lm), &["x", "y"]);
130        assert_eq!(lmap_get(&lm, "x"), Some(&99));
131    }
132
133    #[test]
134    fn test_remove() {
135        let mut lm: LinkedMap<u32> = new_linked_map();
136        lmap_insert(&mut lm, "a", 1);
137        lmap_insert(&mut lm, "b", 2);
138        assert_eq!(lmap_remove(&mut lm, "a"), Some(1));
139        assert_eq!(lmap_keys(&lm), &["b"]);
140    }
141
142    #[test]
143    fn test_contains() {
144        let mut lm: LinkedMap<u32> = new_linked_map();
145        lmap_insert(&mut lm, "k", 0);
146        assert!(lmap_contains(&lm, "k"));
147        assert!(!lmap_contains(&lm, "z"));
148    }
149
150    #[test]
151    fn test_len_and_empty() {
152        let mut lm: LinkedMap<u32> = new_linked_map();
153        assert!(lmap_is_empty(&lm));
154        lmap_insert(&mut lm, "a", 1);
155        assert_eq!(lmap_len(&lm), 1);
156    }
157
158    #[test]
159    fn test_values_ordered() {
160        let mut lm: LinkedMap<u32> = new_linked_map();
161        lmap_insert(&mut lm, "a", 10);
162        lmap_insert(&mut lm, "b", 20);
163        assert_eq!(lmap_values(&lm), vec![&10, &20]);
164    }
165
166    #[test]
167    fn test_get_at() {
168        let mut lm: LinkedMap<u32> = new_linked_map();
169        lmap_insert(&mut lm, "first", 1);
170        lmap_insert(&mut lm, "second", 2);
171        let (k, v) = lmap_get_at(&lm, 1).expect("should succeed");
172        assert_eq!(k, "second");
173        assert_eq!(*v, 2);
174    }
175
176    #[test]
177    fn test_clear() {
178        let mut lm: LinkedMap<u32> = new_linked_map();
179        lmap_insert(&mut lm, "a", 1);
180        lmap_clear(&mut lm);
181        assert!(lmap_is_empty(&lm));
182    }
183}