rucksack_lib/
util.rs

1use std::collections::HashSet;
2
3use anyhow::Result;
4use rand::Rng;
5
6const SPECIALS: &[u8] = b"!@#%&*?=+:";
7
8// If all of elements in the query data set are present in the
9// reference data set, return `true`.
10pub fn all(reference: Vec<String>, query: Vec<String>) -> bool {
11    let r = make_string_set(reference);
12    let q = make_string_set(query);
13    q.is_subset(&r)
14}
15
16// If any of the elements in the query data set are present in the
17// reference data set, return `true`.
18pub fn any(reference: Vec<String>, query: Vec<String>) -> bool {
19    let r = make_string_set(reference);
20    let q = make_string_set(query);
21    if r.intersection(&q).count() == 0 {
22        return false;
23    }
24    true
25}
26
27pub fn bincode_cfg() -> bincode::config::Configuration<
28    bincode::config::LittleEndian,
29    bincode::config::Fixint,
30    bincode::config::NoLimit,
31> {
32    bincode::config::legacy()
33}
34
35pub fn display(text: &str) -> Result<()> {
36    println!("{text}");
37    Ok(())
38}
39
40pub fn make_string_set(input: Vec<String>) -> HashSet<String> {
41    input.into_iter().collect()
42}
43
44pub fn random_specials(count: usize) -> Vec<u8> {
45    let mut specials: Vec<u8> = Vec::new();
46    let mut rng = rand::thread_rng();
47    for _ in 1..count + 1 {
48        specials.push(SPECIALS[rng.gen_range(0..SPECIALS.len())])
49    }
50    specials
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    fn refset() -> Vec<String> {
58        ["a", "b", "c", "d", "e", "f"]
59            .iter()
60            .map(|e| e.to_string())
61            .collect()
62    }
63
64    fn query1() -> Vec<String> {
65        vec!["b".to_string(), "e".to_string()]
66    }
67
68    fn query2() -> Vec<String> {
69        vec!["b".to_string(), "g".to_string()]
70    }
71
72    fn query3() -> Vec<String> {
73        vec!["h".to_string(), "g".to_string()]
74    }
75
76    #[test]
77    fn all() {
78        assert!(super::all(refset(), query1()));
79        assert!(!super::all(refset(), query2()));
80        assert!(!super::all(refset(), query3()));
81    }
82
83    #[test]
84    fn test_all_empty_query() {
85        assert!(super::all(refset(), vec![]), "Empty query should be subset");
86    }
87
88    #[test]
89    fn test_all_empty_reference() {
90        assert!(
91            !super::all(vec![], query1()),
92            "Non-empty query can't be subset of empty reference"
93        );
94    }
95
96    #[test]
97    fn test_all_both_empty() {
98        assert!(
99            super::all(vec![], vec![]),
100            "Empty set is subset of empty set"
101        );
102    }
103
104    #[test]
105    fn test_all_identical_sets() {
106        let set = vec!["x".to_string(), "y".to_string()];
107        assert!(super::all(set.clone(), set));
108    }
109
110    #[test]
111    fn test_all_single_element() {
112        assert!(super::all(refset(), vec!["a".to_string()]));
113        assert!(!super::all(refset(), vec!["z".to_string()]));
114    }
115
116    #[test]
117    fn any() {
118        assert!(super::any(refset(), query1()));
119        assert!(super::any(refset(), query2()));
120        assert!(!super::any(refset(), query3()));
121    }
122
123    #[test]
124    fn test_any_empty_query() {
125        assert!(
126            !super::any(refset(), vec![]),
127            "Empty query has no intersection"
128        );
129    }
130
131    #[test]
132    fn test_any_empty_reference() {
133        assert!(
134            !super::any(vec![], query1()),
135            "Empty reference has no intersection"
136        );
137    }
138
139    #[test]
140    fn test_any_both_empty() {
141        assert!(
142            !super::any(vec![], vec![]),
143            "Empty sets have no intersection"
144        );
145    }
146
147    #[test]
148    fn test_any_single_match() {
149        let reference = vec!["a".to_string(), "b".to_string()];
150        let query = vec!["b".to_string(), "c".to_string()];
151        assert!(super::any(reference, query));
152    }
153
154    #[test]
155    fn test_any_no_match() {
156        let reference = vec!["a".to_string(), "b".to_string()];
157        let query = vec!["x".to_string(), "y".to_string()];
158        assert!(!super::any(reference, query));
159    }
160
161    #[test]
162    fn test_make_string_set_basic() {
163        let input = vec!["a".to_string(), "b".to_string(), "c".to_string()];
164        let set = make_string_set(input);
165        assert_eq!(set.len(), 3);
166        assert!(set.contains("a"));
167        assert!(set.contains("b"));
168        assert!(set.contains("c"));
169    }
170
171    #[test]
172    fn test_make_string_set_duplicates() {
173        let input = vec!["a".to_string(), "a".to_string(), "b".to_string()];
174        let set = make_string_set(input);
175        assert_eq!(set.len(), 2, "Duplicates should be removed");
176    }
177
178    #[test]
179    fn test_make_string_set_empty() {
180        let input: Vec<String> = vec![];
181        let set = make_string_set(input);
182        assert!(set.is_empty());
183    }
184
185    #[test]
186    fn test_display_simple() {
187        let result = display("Hello, World!");
188        assert!(result.is_ok());
189    }
190
191    #[test]
192    fn test_display_empty() {
193        let result = display("");
194        assert!(result.is_ok());
195    }
196
197    #[test]
198    fn test_display_multiline() {
199        let result = display("Line 1\nLine 2\nLine 3");
200        assert!(result.is_ok());
201    }
202
203    #[test]
204    fn test_display_unicode() {
205        let result = display("Hello δΈ–η•Œ 🌍");
206        assert!(result.is_ok());
207    }
208
209    #[test]
210    fn test_random_specials_zero() {
211        let specials = random_specials(0);
212        assert_eq!(specials.len(), 0);
213    }
214
215    #[test]
216    fn test_random_specials_one() {
217        let specials = random_specials(1);
218        assert_eq!(specials.len(), 1);
219        assert!(SPECIALS.contains(&specials[0]));
220    }
221
222    #[test]
223    fn test_random_specials_multiple() {
224        let specials = random_specials(10);
225        assert_eq!(specials.len(), 10);
226        for &s in &specials {
227            assert!(
228                SPECIALS.contains(&s),
229                "All returned characters should be from SPECIALS"
230            );
231        }
232    }
233
234    #[test]
235    fn test_random_specials_all_valid() {
236        let specials = random_specials(100);
237        assert_eq!(specials.len(), 100);
238        for &s in &specials {
239            assert!(SPECIALS.contains(&s));
240        }
241    }
242
243    #[test]
244    fn test_bincode_cfg_returns_config() {
245        let _config = bincode_cfg();
246        // Just ensure it doesn't panic and returns something
247    }
248
249    #[test]
250    fn test_bincode_cfg_is_legacy() {
251        let config = bincode_cfg();
252        // Verify it's the legacy configuration by using it
253        let data = vec![1u8, 2u8, 3u8];
254        let encoded = bincode::encode_to_vec(&data, config).unwrap();
255        let (decoded, _): (Vec<u8>, _) = bincode::decode_from_slice(&encoded, config).unwrap();
256        assert_eq!(data, decoded);
257    }
258
259    #[test]
260    fn test_specials_constant() {
261        assert_eq!(SPECIALS, b"!@#%&*?=+:");
262        assert_eq!(SPECIALS.len(), 10);
263    }
264}