1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
use rand::{thread_rng, Rng};
use std::collections::HashMap;

/// AutoIdMap is a wrapper around HashMap which automatically creates a unique id for it's entries
/// # Example
/// ```no_run
///
/// use hirofa_utils::auto_id_map::AutoIdMap;
/// let mut map = AutoIdMap::new();
/// let id1 = map.insert("hi");
/// let id2 = map.insert("hi2");
/// assert_ne!(id1, id2);
/// assert_eq!(map.len(), 2);
/// let s1 = map.remove(&id1);
/// assert_eq!(s1, "hi");
/// assert_eq!(map.len(), 1);
/// ```
pub struct AutoIdMap<T> {
    max_size: usize,
    pub map: HashMap<usize, T>,
}

impl<T> AutoIdMap<T> {
    /// create a new instance of the AutoIdMap
    pub fn new() -> AutoIdMap<T> {
        Self::new_with_max_size(usize::MAX)
    }

    pub fn new_with_max_size(max_size: usize) -> AutoIdMap<T> {
        AutoIdMap {
            max_size,
            map: HashMap::new(),
        }
    }

    pub fn foreach_value<F: Fn(&T)>(&self, f: F) {
        for i in self.map.values() {
            f(i);
        }
    }

    pub fn foreach_value_mut<F: Fn(&mut T)>(&mut self, f: F) {
        for i in self.map.values_mut() {
            f(i);
        }
    }

    pub fn foreach<F: Fn(&usize, &T)>(&self, f: F) {
        for i in &self.map {
            f(i.0, i.1);
        }
    }

    pub fn clear(&mut self) {
        self.map.clear();
    }

    pub fn remove_values<F: Fn(&T) -> bool>(&mut self, f: F) -> Vec<T> {
        let mut rems = vec![];
        let mut rem_keys = vec![];
        {
            for i in self.map.iter() {
                if f(i.1) {
                    rem_keys.push(*i.0);
                }
            }
        }
        for k in rem_keys {
            rems.push(self.map.remove(&k).unwrap());
        }
        rems
    }

    pub fn contains_value<F: Fn(&T) -> bool>(&self, f: F) -> bool {
        for v in self.map.values() {
            if f(v) {
                return true;
            }
        }
        false
    }

    /// insert an element and return the new id
    pub fn insert(&mut self, elem: T) -> usize {
        self.try_insert(elem).expect("map is full")
    }

    /// insert an element and return the new id
    pub fn try_insert(&mut self, elem: T) -> Result<usize, &str> {
        if self.map.len() >= self.max_size {
            Err("AutoIdMap is full")
        } else {
            let mut id = thread_rng().gen_range(0..self.max_size);

            while self.map.contains_key(&id) {
                if id >= self.max_size - 1 {
                    id = 0;
                } else {
                    id += 1;
                }
            }

            self.map.insert(id, elem);
            Ok(id)
        }
    }

    /// replace an element, this will panic if you pass an id that is not present
    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn replace(&mut self, id: &usize, elem: T) {
        // because we really don't want you to abuse this to insert your own id's :)
        if !self.contains_key(id) {
            panic!("no entry to replace for {}", id);
        }
        self.map.insert(*id, elem);
    }

    /// get an element based on it's id
    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn get(&self, id: &usize) -> Option<&T> {
        self.map.get(id)
    }

    /// get an element based on it's id
    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn get_mut(&mut self, id: &usize) -> Option<&mut T> {
        self.map.get_mut(id)
    }

    /// remove an element based on its id
    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn remove(&mut self, id: &usize) -> T {
        self.map.remove(id).expect("no such elem")
    }

    /// remove an element based on its id
    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn remove_opt(&mut self, id: &usize) -> Option<T> {
        self.map.remove(id)
    }

    /// get the size of the map
    #[allow(dead_code)]
    pub fn len(&self) -> usize {
        self.map.len()
    }

    /// see if map is empty
    #[allow(dead_code)]
    pub fn is_empty(&self) -> bool {
        self.map.is_empty()
    }

    /// check if a map contains a certain id
    #[allow(clippy::trivially_copy_pass_by_ref)]
    pub fn contains_key(&self, id: &usize) -> bool {
        self.map.contains_key(id)
    }
}

impl<T> Default for AutoIdMap<T> {
    fn default() -> Self {
        AutoIdMap::new()
    }
}

#[cfg(test)]
pub mod tests {
    use crate::auto_id_map::AutoIdMap;

    #[test]
    fn test_aim() {
        let mut map = AutoIdMap::new_with_max_size(8);
        for _x in 0..8 {
            map.insert("foo");
        }
        assert_eq!(map.len(), 8);
        map.remove(&5);
        let free_id = map.insert("fail?");

        assert_eq!(free_id, 5);
    }

    #[test]
    fn test_aim_ms() {
        let mut map = AutoIdMap::new_with_max_size(8);
        for _x in 0..8 {
            map.insert("foo");
        }
        assert_eq!(map.len(), 8);
        map.remove(&5);
        let free_id = map.insert("fail?");

        assert_eq!(free_id, 5);

        let res = map.try_insert("foobar");
        // should be full
        assert!(res.is_err());
    }
}