tinybase/
constraint.rs

1use crate::{
2    index::{AnyIndex, IndexType},
3    table::TableType,
4    Index,
5};
6
7use alloc::boxed::Box;
8#[allow(unused_imports)]
9use alloc::string::String;
10
11/// Represents a constraint on a typed table.
12pub struct Constraint<T: TableType + 'static>(pub(crate) ConstraintInner<T>);
13
14pub(crate) enum ConstraintInner<T: TableType + 'static> {
15    /// Unique constraint based on index.
16    Unique(Box<dyn AnyIndex<T>>),
17    /// Constraint based on closure check.
18    Check(fn(&T) -> bool),
19}
20
21impl<T: TableType> Constraint<T> {
22    /// Creates a new unique constraint using the given index.
23    ///
24    /// # Arguments
25    ///
26    /// * `index` - A reference to the [`Index`] instance to be used for enforcing the unique constraint.
27    pub fn unique<I: IndexType + 'static>(index: &Index<T, I>) -> Self {
28        Self(ConstraintInner::Unique(Box::new(index.clone())))
29    }
30
31    /// Creates a new constraint based on a custom check function.
32    ///
33    /// # Arguments
34    ///
35    /// * `check` - A function that takes a reference to the value `T` and returns a boolean indicating if the constraint is satisfied.
36    pub fn check(check: fn(&T) -> bool) -> Self {
37        Self(ConstraintInner::Check(check))
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use alloc::borrow::ToOwned;
44
45    use super::*;
46    use crate::{result::TinyBaseError, Table, TinyBase};
47
48    #[test]
49    fn table_constraint() {
50        let db = TinyBase::new(None, true);
51        let table: Table<String> = db.open_table("test_table").unwrap();
52
53        // Create an index for the constraint
54        let index = table
55            .create_index("name", |value| value.to_owned())
56            .unwrap();
57
58        // Add unique constraint with created index
59        assert!(table.constraint(Constraint::unique(&index)).is_ok());
60
61        // Add check constraint with condition
62        assert!(table
63            .constraint(Constraint::check(|value: &String| value.len() >= 5))
64            .is_ok());
65
66        table.insert("greater".to_owned()).unwrap();
67
68        // Unique constraint.
69        assert!(matches!(
70            table.insert("greater".to_owned()),
71            Err(TinyBaseError::Exists { .. })
72        ));
73
74        // Check constraint.
75        assert!(matches!(
76            table.insert("less".to_owned()),
77            Err(TinyBaseError::Condition)
78        ));
79    }
80}