composable_indexes/
lib.rs

1//! A library for in-memory collections with flexible and composable indexes.
2//!
3//! This crate provides a framework for building collections with multiple indexes
4//! that can be combined and composed. Key features include:
5//!
6//! - Built-in indexes for common use cases (BTree, HashTable)
7//! - Composable index combinators (grouping, filtering, mapping)
8//! - Aggregation indexes for statistical operations
9//! - Safe and efficient index maintenance as collection changes
10//!
11//! # Example
12//! ```rust
13//! use composable_indexes::{Collection, index};
14//!
15//! // A struct representing a person
16//! struct Person { name: String, age: u32, occupation: String }
17//!
18//! // Create a collection indexed by age using premap
19//! let mut collection = Collection::<Person, _>::new(
20//!   index::zip!(
21//!     index::premap(|p: &Person| p.age, index::btree()),
22//!     index::premap(|p: &Person| p.occupation.clone(), index::hashtable()),
23//!   )
24//! );
25//!
26//! // insert & update collection
27//! let alice = collection.insert(Person { name: "Alice".to_string(), age: 30, occupation: "Engineer".to_string() });
28//! collection.insert(Person { name: "Bob".to_string(), age: 25, occupation: "Designer".to_string() });
29//! collection.adjust_mut(alice, |p| { p.age = 31; });
30//! // ...
31//!
32//! let q = collection.query();
33//!
34//! // Query oldest person
35//! let _youngest = q.0.max_one();
36//!
37//! // Query the number of unique occupations
38//! let _occupation_count = q.1.count_distinct();
39
40pub use composable_indexes_core::{Collection, Key};
41
42pub mod aggregation;
43pub mod index;
44
45// Some tests for the Collection functionality is defined
46// here so we can utilise the testutils crate.
47#[cfg(test)]
48mod test {
49    use composable_indexes_core::*;
50    use composable_indexes_testutils::{op_insert, op_remove, op_update, test_index};
51
52    #[test]
53    fn simple() {
54        let mut db = Collection::<u32, _>::new(test_index());
55
56        let one = db.insert(1);
57        let two = db.insert(2);
58        let three = db.insert(3);
59        db.update(two, |_| 10);
60        let four = db.insert(4);
61        db.delete(&three);
62
63        assert_eq!(db.get(one), Some(&1));
64        assert_eq!(db.get(two), Some(&10));
65        assert_eq!(db.get(three), None);
66        assert_eq!(db.get(four), Some(&4));
67        assert_eq!(db.len(), 3);
68
69        let q = db.query();
70        assert_eq!(
71            q.operations(),
72            vec![
73                op_insert!(0, 1),
74                op_insert!(1, 2),
75                op_insert!(2, 3),
76                op_update!(1, 2, 10),
77                op_insert!(3, 4),
78                op_remove!(2, 3),
79            ]
80        );
81    }
82
83    #[test]
84    fn update_mut_updates() {
85        let mut db = Collection::<u32, _>::new(test_index());
86
87        let one = db.insert(1);
88        db.update_mut(one, |v| {
89            if let Some(v) = v {
90                *v += 1;
91            }
92        });
93
94        assert_eq!(db.get(one), Some(&2));
95        assert_eq!(db.len(), 1);
96        assert_eq!(
97            db.query().operations(),
98            vec![op_insert!(0, 1), op_remove!(0, 1), op_insert!(0, 2),]
99        );
100    }
101
102    #[test]
103    fn update_mut_inserts() {
104        let mut db = Collection::<u32, _>::new(test_index());
105
106        let one = db.insert(1);
107        db.delete(&one);
108        db.update_mut(one, |v| {
109            assert!(v.is_none());
110            *v = Some(2);
111        });
112
113        assert_eq!(db.get(one), Some(&2));
114        assert_eq!(db.len(), 1);
115        assert_eq!(
116            db.query().operations(),
117            vec![op_insert!(0, 1), op_remove!(0, 1), op_insert!(0, 2),]
118        );
119    }
120
121    #[test]
122    fn update_mut_removes() {
123        let mut db = Collection::<u32, _>::new(test_index());
124
125        let one = db.insert(1);
126        db.update_mut(one, |v| {
127            assert!(v.is_some());
128            *v = None;
129        });
130
131        assert_eq!(db.get(one), None);
132        assert_eq!(db.len(), 0);
133        assert_eq!(
134            db.query().operations(),
135            vec![op_insert!(0, 1), op_remove!(0, 1),]
136        );
137    }
138
139    #[test]
140    fn update_updates() {
141        let mut db = Collection::<u32, _>::new(test_index());
142
143        let one = db.insert(1);
144        db.update(one, |_| 2);
145
146        assert_eq!(db.get(one), Some(&2));
147        assert_eq!(db.len(), 1);
148        assert_eq!(
149            db.query().operations(),
150            vec![op_insert!(0, 1), op_update!(0, 1, 2),]
151        );
152    }
153
154    #[test]
155    fn update_inserts() {
156        let mut db = Collection::<u32, _>::new(test_index());
157
158        let one = db.insert(1);
159        db.delete(&one);
160
161        db.update(one, |x| {
162            assert_eq!(x, None);
163            2
164        });
165
166        assert_eq!(db.get(one), Some(&2));
167        assert_eq!(db.len(), 1);
168        assert_eq!(
169            db.query().operations(),
170            vec![op_insert!(0, 1), op_remove!(0, 1), op_insert!(0, 2),]
171        );
172    }
173
174    #[test]
175    fn adjust_mut_updates() {
176        let mut db = Collection::<u32, _>::new(test_index());
177
178        let one = db.insert(1);
179        db.adjust_mut(one, |v| {
180            *v = 2;
181        });
182
183        assert_eq!(db.get(one), Some(&2));
184        assert_eq!(db.len(), 1);
185        assert_eq!(
186            db.query().operations(),
187            vec![op_insert!(0, 1), op_remove!(0, 1), op_insert!(0, 2),]
188        );
189    }
190
191    #[test]
192    fn adjust_mut_ignores_non_existent() {
193        let mut db = Collection::<u32, _>::new(test_index());
194
195        let one = db.insert(1);
196        db.delete(&one);
197
198        db.adjust_mut(one, |_| {
199            panic!("Should not be called");
200        });
201
202        assert_eq!(db.get(one), None);
203        assert_eq!(db.len(), 0);
204        assert_eq!(
205            db.query().operations(),
206            vec![op_insert!(0, 1), op_remove!(0, 1),]
207        );
208    }
209
210    #[test]
211    fn adjust_updates() {
212        let mut db = Collection::<u32, _>::new(test_index());
213
214        let one = db.insert(1);
215        db.adjust(one, |_| 2);
216
217        assert_eq!(db.get(one), Some(&2));
218        assert_eq!(db.len(), 1);
219        assert_eq!(
220            db.query().operations(),
221            vec![op_insert!(0, 1), op_update!(0, 1, 2),]
222        );
223    }
224
225    #[test]
226    fn adjust_ignores_non_existent() {
227        let mut db = Collection::<u32, _>::new(test_index());
228
229        let one = db.insert(1);
230        db.delete(&one);
231
232        db.adjust(one, |_| 2);
233
234        assert_eq!(db.get(one), None);
235        assert_eq!(db.len(), 0);
236        assert_eq!(
237            db.query().operations(),
238            vec![op_insert!(0, 1), op_remove!(0, 1),]
239        );
240    }
241}