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_by_key_mut(alice, |p| { p.age = 31; });
30//! // ...
31//!
32//! // Query oldest person
33//! let _oldest = collection.query(|ix| ix._1().inner().max_one());
34//!
35//! // Query the number of unique occupations
36//! let _occupation_count = collection.query(|ix| ix._2().inner().count_distinct());
37//! ```
38
39pub use composable_indexes_core::{Collection, Key};
40
41pub mod aggregation;
42pub mod index;
43
44// Some tests for the Collection functionality is defined
45// here so we can utilise the testutils crate.
46#[cfg(test)]
47mod test {
48    use composable_indexes_core::*;
49    use composable_indexes_testutils::{op_insert, op_remove, op_update, test_index};
50
51    #[test]
52    fn simple() {
53        let mut db = Collection::<u32, _>::new(test_index());
54
55        let one = db.insert(1);
56        let two = db.insert(2);
57        let three = db.insert(3);
58        db.update_by_key(two, |_| 10);
59        let four = db.insert(4);
60        db.delete_by_key(&three);
61
62        assert_eq!(db.get_by_key(one), Some(&1));
63        assert_eq!(db.get_by_key(two), Some(&10));
64        assert_eq!(db.get_by_key(three), None);
65        assert_eq!(db.get_by_key(four), Some(&4));
66        assert_eq!(db.len(), 3);
67
68        // Access test index operations directly
69        let ops = db.query(|ix| Plain(ix.ops.clone()));
70        assert_eq!(
71            ops,
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_by_key_mut(one, |v| {
89            if let Some(v) = v {
90                *v += 1;
91            }
92        });
93
94        assert_eq!(db.get_by_key(one), Some(&2));
95        assert_eq!(db.len(), 1);
96        let ops = db.query(|ix| Plain(ix.ops.clone()));
97        assert_eq!(
98            ops,
99            vec![op_insert!(0, 1), op_remove!(0, 1), op_insert!(0, 2),]
100        );
101    }
102
103    #[test]
104    fn update_mut_inserts() {
105        let mut db = Collection::<u32, _>::new(test_index());
106
107        let one = db.insert(1);
108        db.delete_by_key(&one);
109        db.update_by_key_mut(one, |v| {
110            assert!(v.is_none());
111            *v = Some(2);
112        });
113
114        assert_eq!(db.get_by_key(one), Some(&2));
115        assert_eq!(db.len(), 1);
116        let ops = db.query(|ix| Plain(ix.ops.clone()));
117        assert_eq!(
118            ops,
119            vec![op_insert!(0, 1), op_remove!(0, 1), op_insert!(0, 2),]
120        );
121    }
122
123    #[test]
124    fn update_mut_removes() {
125        let mut db = Collection::<u32, _>::new(test_index());
126
127        let one = db.insert(1);
128        db.update_by_key_mut(one, |v| {
129            assert!(v.is_some());
130            *v = None;
131        });
132
133        assert_eq!(db.get_by_key(one), None);
134        assert_eq!(db.len(), 0);
135        let ops = db.query(|ix| Plain(ix.ops.clone()));
136        assert_eq!(ops, vec![op_insert!(0, 1), op_remove!(0, 1),]);
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_by_key(one, |_| 2);
145
146        assert_eq!(db.get_by_key(one), Some(&2));
147        assert_eq!(db.len(), 1);
148        let ops = db.query(|ix| Plain(ix.ops.clone()));
149        assert_eq!(ops, vec![op_insert!(0, 1), op_update!(0, 1, 2),]);
150    }
151
152    #[test]
153    fn update_inserts() {
154        let mut db = Collection::<u32, _>::new(test_index());
155
156        let one = db.insert(1);
157        db.delete_by_key(&one);
158
159        db.update_by_key(one, |x| {
160            assert_eq!(x, None);
161            2
162        });
163
164        assert_eq!(db.get_by_key(one), Some(&2));
165        assert_eq!(db.len(), 1);
166        let ops = db.query(|ix| Plain(ix.ops.clone()));
167        assert_eq!(
168            ops,
169            vec![op_insert!(0, 1), op_remove!(0, 1), op_insert!(0, 2),]
170        );
171    }
172
173    #[test]
174    fn adjust_mut_updates() {
175        let mut db = Collection::<u32, _>::new(test_index());
176
177        let one = db.insert(1);
178        db.adjust_by_key_mut(one, |v| {
179            *v = 2;
180        });
181
182        assert_eq!(db.get_by_key(one), Some(&2));
183        assert_eq!(db.len(), 1);
184        let ops = db.query(|ix| Plain(ix.ops.clone()));
185        assert_eq!(
186            ops,
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_by_key(&one);
197
198        db.adjust_by_key_mut(one, |_| {
199            panic!("Should not be called");
200        });
201
202        assert_eq!(db.get_by_key(one), None);
203        assert_eq!(db.len(), 0);
204        let ops = db.query(|ix| Plain(ix.ops.clone()));
205        assert_eq!(ops, vec![op_insert!(0, 1), op_remove!(0, 1),]);
206    }
207
208    #[test]
209    fn adjust_updates() {
210        let mut db = Collection::<u32, _>::new(test_index());
211
212        let one = db.insert(1);
213        db.adjust_by_key(one, |_| 2);
214
215        assert_eq!(db.get_by_key(one), Some(&2));
216        assert_eq!(db.len(), 1);
217        let ops = db.query(|ix| Plain(ix.ops.clone()));
218        assert_eq!(ops, vec![op_insert!(0, 1), op_update!(0, 1, 2),]);
219    }
220
221    #[test]
222    fn adjust_ignores_non_existent() {
223        let mut db = Collection::<u32, _>::new(test_index());
224
225        let one = db.insert(1);
226        db.delete_by_key(&one);
227
228        db.adjust_by_key(one, |_| 2);
229
230        assert_eq!(db.get_by_key(one), None);
231        assert_eq!(db.len(), 0);
232        let ops = db.query(|ix| Plain(ix.ops.clone()));
233        assert_eq!(ops, vec![op_insert!(0, 1), op_remove!(0, 1),]);
234    }
235}