Skip to content

Commit 3574cf9

Browse files
committed
refactor(deep_causality): updated TagIndex to use a HashSet to prevent duplicate tags.
Signed-off-by: Marvin Hansen <[email protected]>
1 parent 6f90deb commit 3574cf9

File tree

3 files changed

+60
-38
lines changed

3 files changed

+60
-38
lines changed

deep_causality/src/types/telos_types/tag_index/index.rs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
*/
55

66
use crate::{TagIndex, TeloidID, TeloidTag};
7+
use std::collections::HashSet;
78

89
impl TagIndex {
910
/// Adds a `TeloidID` to the index for a given `TeloidTag`.
1011
///
1112
/// If the tag does not exist in the index, it will be added.
13+
/// If the ID already exists for the given tag, it will not be added again.
1214
///
1315
/// # Arguments
1416
///
@@ -24,36 +26,42 @@ impl TagIndex {
2426
/// let tag = "test_tag";
2527
/// let id = 1;
2628
/// tag_index.add(tag, id);
29+
/// tag_index.add(tag, id); // Duplicates are ignored
30+
/// assert_eq!(tag_index.get(tag).unwrap().len(), 1);
2731
/// ```
2832
pub fn add(&mut self, tag: TeloidTag, id: TeloidID) {
29-
self.index.entry(tag).or_default().push(id);
33+
self.index.entry(tag).or_default().insert(id);
3034
}
3135

32-
/// Retrieves a vector of `TeloidID`s associated with a given `TeloidTag`.
36+
/// Retrieves a reference to the set of `TeloidID`s associated with a given `TeloidTag`.
3337
///
3438
/// # Arguments
3539
///
3640
/// * `tag` - The `TeloidTag` to look up.
3741
///
3842
/// # Returns
3943
///
40-
/// An `Option` containing a reference to the vector of `TeloidID`s if the tag exists,
44+
/// An `Option` containing a reference to the `HashSet` of `TeloidID`s if the tag exists,
4145
/// otherwise `None`.
4246
///
4347
/// # Examples
4448
///
4549
/// ```
4650
/// use deep_causality::{TagIndex, TeloidTag, TeloidID};
51+
/// use std::collections::HashSet;
4752
///
4853
/// let mut tag_index = TagIndex::new();
4954
/// let tag = "test_tag";
5055
/// let id = 1;
51-
/// tag_index.add(tag.clone(), id);
56+
/// tag_index.add(tag, id);
57+
///
58+
/// let mut expected = HashSet::new();
59+
/// expected.insert(1);
5260
///
5361
/// let ids = tag_index.get(tag);
54-
/// assert_eq!(ids, Some(&vec![1]));
62+
/// assert_eq!(ids, Some(&expected));
5563
/// ```
56-
pub fn get(&self, tag: &str) -> Option<&Vec<TeloidID>> {
64+
pub fn get(&self, tag: &str) -> Option<&HashSet<TeloidID>> {
5765
self.index.get(tag)
5866
}
5967

@@ -74,12 +82,16 @@ impl TagIndex {
7482
/// let mut tag_index = TagIndex::new();
7583
/// let tag = "test_tag";
7684
/// let id = 1;
77-
/// tag_index.add(tag.clone(), id);
85+
/// tag_index.add(tag, id);
7886
/// tag_index.remove(tag, id);
87+
/// assert!(tag_index.get(tag).is_none() || tag_index.get(tag).unwrap().is_empty());
7988
/// ```
8089
pub fn remove(&mut self, tag: &str, id: TeloidID) {
81-
if let Some(v) = self.index.get_mut(tag) {
82-
v.retain(|x| *x != id);
90+
if let Some(set) = self.index.get_mut(tag) {
91+
set.remove(&id);
92+
if set.is_empty() {
93+
self.index.remove(tag);
94+
}
8395
}
8496
}
8597

@@ -101,12 +113,13 @@ impl TagIndex {
101113
/// let tag = "test_tag";
102114
/// let id1 = 1;
103115
/// let id2 = 2;
104-
/// tag_index.add(tag.clone(), id1);
116+
/// tag_index.add(tag, id1);
105117
/// tag_index.update(tag, id2);
118+
/// assert_eq!(tag_index.get(tag).unwrap().len(), 2);
106119
/// ```
107120
pub fn update(&mut self, tag: &str, id: TeloidID) {
108121
if let Some(v) = self.index.get_mut(tag) {
109-
v.push(id);
122+
v.insert(id);
110123
}
111124
}
112125

@@ -127,7 +140,7 @@ impl TagIndex {
127140
///
128141
/// let mut tag_index = TagIndex::new();
129142
/// let tag = "test_tag";
130-
/// tag_index.add(tag.clone(), 1);
143+
/// tag_index.add(tag, 1);
131144
///
132145
/// assert!(tag_index.contains_key(tag));
133146
/// ```

deep_causality/src/types/telos_types/tag_index/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
*/
55

66
use crate::{TeloidID, TeloidTag};
7-
use std::collections::HashMap;
7+
use std::collections::{HashMap, HashSet};
88

99
mod display;
1010
mod index;
1111

1212
#[derive(Debug, Default, Clone, PartialEq, Eq)]
1313
pub struct TagIndex {
14-
index: HashMap<TeloidTag, Vec<TeloidID>>,
14+
index: HashMap<TeloidTag, HashSet<TeloidID>>,
1515
}
1616

1717
impl TagIndex {

deep_causality/tests/types/telos_types/tag_index/tag_index_tests.rs

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,23 @@
55

66
use deep_causality::types::telos_types::tag_index::*;
77
use deep_causality::{TeloidID, TeloidTag};
8+
use std::collections::HashSet;
89

910
#[test]
1011
fn test_new_and_default() {
1112
let tag_index_new = TagIndex::new();
12-
assert_eq!(tag_index_new.len(), 0);
13+
assert!(tag_index_new.is_empty());
1314
assert!(tag_index_new.get("1").is_none());
1415

1516
let tag_index_default = TagIndex::default();
16-
assert_eq!(tag_index_default.len(), 0);
17+
assert!(tag_index_default.is_empty());
1718
assert_eq!(tag_index_new, tag_index_default);
1819
}
1920

2021
#[test]
2122
fn test_with_capacity() {
2223
let tag_index = TagIndex::with_capacity(10);
23-
assert_eq!(tag_index.len(), 0);
24+
assert!(tag_index.is_empty());
2425
}
2526

2627
#[test]
@@ -32,28 +33,28 @@ fn test_add_and_get() {
3233
// 1. Add a single item
3334
tag_index.add(tag1, id1);
3435
assert_eq!(tag_index.len(), 1);
35-
assert_eq!(tag_index.get(tag1), Some(&vec![id1]));
36+
assert_eq!(tag_index.get(tag1), Some(&HashSet::from([id1])));
3637

3738
// 2. Add another item to the same tag
3839
let id2: TeloidID = 102;
3940
tag_index.add(tag1, id2);
4041
assert_eq!(tag_index.len(), 1);
41-
assert_eq!(tag_index.get(tag1), Some(&vec![id1, id2]));
42+
assert_eq!(tag_index.get(tag1), Some(&HashSet::from([id1, id2])));
4243

4344
// 3. Add a new tag
4445
let tag2: TeloidTag = "tag2";
4546
let id3: TeloidID = 103;
4647
tag_index.add(tag2, id3);
4748
assert_eq!(tag_index.len(), 2);
48-
assert_eq!(tag_index.get(tag2), Some(&vec![id3]));
49+
assert_eq!(tag_index.get(tag2), Some(&HashSet::from([id3])));
4950

5051
// 4. Get a non-existent tag (corner case)
5152
let tag_non_existent: TeloidTag = "tag99";
5253
assert!(tag_index.get(tag_non_existent).is_none());
5354
}
5455

5556
#[test]
56-
fn test_add_duplicate_id_edge_case() {
57+
fn test_add_duplicate_id_is_ignored() {
5758
let mut tag_index = TagIndex::new();
5859
let tag: TeloidTag = "tag1";
5960
let id: TeloidID = 101;
@@ -62,7 +63,12 @@ fn test_add_duplicate_id_edge_case() {
6263
tag_index.add(tag, id); // Add the exact same ID again
6364

6465
assert_eq!(tag_index.len(), 1);
65-
assert_eq!(tag_index.get(tag), Some(&vec![id, id]));
66+
assert_eq!(tag_index.get(tag), Some(&HashSet::from([id])));
67+
assert_eq!(
68+
tag_index.get(tag).unwrap().len(),
69+
1,
70+
"HashSet should contain only one unique ID"
71+
);
6672
}
6773

6874
#[test]
@@ -76,22 +82,21 @@ fn test_remove() {
7682

7783
// 1. Remove an existing ID
7884
tag_index.remove(tag1, id1);
79-
assert_eq!(tag_index.get(tag1), Some(&vec![id2]));
85+
assert_eq!(tag_index.get(tag1), Some(&HashSet::from([id2])));
8086

8187
// 2. Remove a non-existent ID from an existing tag (corner case)
8288
tag_index.remove(tag1, 999);
83-
assert_eq!(tag_index.get(tag1), Some(&vec![id2]));
89+
assert_eq!(tag_index.get(tag1), Some(&HashSet::from([id2])));
8490

85-
// 3. Remove the last ID for a tag
91+
// 3. Remove the last ID for a tag, which should remove the tag itself
8692
tag_index.remove(tag1, id2);
87-
// The key should still exist with an empty vec, as per implementation
88-
assert!(tag_index.contains_key(tag1));
89-
assert_eq!(tag_index.get(tag1), Some(&vec![]));
93+
assert!(!tag_index.contains_key(tag1));
94+
assert!(tag_index.get(tag1).is_none());
9095

9196
// 4. Remove an ID from a non-existent tag (corner case)
9297
let tag_non_existent: TeloidTag = "tag99";
9398
tag_index.remove(tag_non_existent, 999);
94-
assert_eq!(tag_index.len(), 1); // Length should be unchanged
99+
assert!(tag_index.is_empty()); // Length should be 0
95100
}
96101

97102
#[test]
@@ -103,20 +108,20 @@ fn test_update() {
103108
// 1. Try to update a non-existent tag (edge case)
104109
tag_index.update(tag1, id1);
105110
assert!(!tag_index.contains_key(tag1));
106-
assert_eq!(tag_index.len(), 0);
111+
assert!(tag_index.is_empty());
107112

108113
// 2. Add the tag first, then update
109114
tag_index.add(tag1, id1);
110-
assert_eq!(tag_index.get(tag1), Some(&vec![id1]));
115+
assert_eq!(tag_index.get(tag1), Some(&HashSet::from([id1])));
111116

112117
let id2: TeloidID = 102;
113118
tag_index.update(tag1, id2);
114119
assert_eq!(tag_index.len(), 1);
115-
assert_eq!(tag_index.get(tag1), Some(&vec![id1, id2]));
120+
assert_eq!(tag_index.get(tag1), Some(&HashSet::from([id1, id2])));
116121
}
117122

118123
#[test]
119-
fn test_check() {
124+
fn test_contains_key() {
120125
let mut tag_index = TagIndex::new();
121126
let tag1: TeloidTag = "tag1";
122127
let id1: TeloidID = 101;
@@ -128,9 +133,9 @@ fn test_check() {
128133
tag_index.add(tag1, id1);
129134
assert!(tag_index.contains_key(tag1));
130135

131-
// 3. Check after removing the only ID (key should still exist)
136+
// 3. Check after removing the only ID (key should be removed)
132137
tag_index.remove(tag1, id1);
133-
assert!(tag_index.contains_key(tag1));
138+
assert!(!tag_index.contains_key(tag1));
134139
}
135140

136141
#[test]
@@ -143,12 +148,12 @@ fn test_clear() {
143148

144149
// 1. Clear the index
145150
tag_index.clear();
146-
assert_eq!(tag_index.len(), 0);
151+
assert!(tag_index.is_empty());
147152
assert!(!tag_index.contains_key("tag1"));
148153

149154
// 2. Clear an already empty index (corner case)
150155
tag_index.clear();
151-
assert_eq!(tag_index.len(), 0);
156+
assert!(tag_index.is_empty());
152157
}
153158

154159
#[test]
@@ -166,10 +171,14 @@ fn test_len() {
166171
tag_index.add("tag1", 102);
167172
assert_eq!(tag_index.len(), 2);
168173

169-
// Removing an ID doesn't change len
174+
// Removing an ID from a tag with multiple IDs doesn't change len
170175
tag_index.remove("tag1", 101);
171176
assert_eq!(tag_index.len(), 2);
172177

178+
// Removing the last ID from a tag *does* change len
179+
tag_index.remove("tag1", 102);
180+
assert_eq!(tag_index.len(), 1);
181+
173182
// Clearing changes len to 0
174183
tag_index.clear();
175184
assert_eq!(tag_index.len(), 0);

0 commit comments

Comments
 (0)