1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! Defines the storage abstraction for relation tuples.
use std::collections::HashSet;
use crate::model::{Object, Relation, RelationTuple, User};
/// A trait for abstracting the storage and retrieval of `RelationTuple`s.
/// This allows the core logic to be decoupled from the specific storage backend.
pub trait TupleStore: Send + Sync {
/// Reads tuples from the store, with optional filtering.
///
/// # Arguments
///
/// * `object` - The object to filter by.
/// * `relation` - An optional relation to filter by.
/// * `user` - An optional user to filter by.
///
/// # Returns
///
/// A vector of matching `RelationTuple`s.
fn read_tuples(
&self,
object: &Object,
relation: Option<&Relation>,
user: Option<&User>,
) -> Vec<RelationTuple>;
/// Writes a single tuple to the store.
///
/// # Returns
///
/// `Ok(())` if the write was successful, or an error string if it failed.
///
/// # Errors
///
/// Returns an error string when the tuple cannot be written, such as attempting to create a
/// duplicate tuple in stores that enforce uniqueness.
fn write_tuple(&mut self, tuple: RelationTuple) -> Result<(), String>;
/// Deletes a single tuple from the store.
///
/// # Returns
///
/// `Ok(())` if the delete was successful, or an error string if it failed.
///
/// # Errors
///
/// Returns an error string when the tuple cannot be deleted, such as when it does not exist.
fn delete_tuple(&mut self, tuple: &RelationTuple) -> Result<(), String>;
/// Returns all tuples currently stored.
///
/// This compatibility method lets the v2 indexed store rebuild from legacy state during the
/// migration period.
fn all_tuples(&self) -> Vec<RelationTuple>;
/// Replaces all tuples currently stored.
///
/// This compatibility method lets the service apply validated batch mutations atomically
/// across the legacy tuple store and the indexed relationship store during the migration
/// period.
fn replace_all(&mut self, tuples: Vec<RelationTuple>);
}
/// A simple, in-memory implementation of the `TupleStore` trait using a `HashSet`.
#[derive(Debug, Default)]
pub struct InMemoryTupleStore {
store: HashSet<RelationTuple>,
}
impl TupleStore for InMemoryTupleStore {
fn read_tuples(
&self,
object: &Object,
relation: Option<&Relation>,
user: Option<&User>,
) -> Vec<RelationTuple> {
self.store
.iter()
.filter(|t| {
t.object == *object
&& relation.is_none_or(|r| t.relation == *r)
&& user.is_none_or(|u| t.user == *u)
})
.cloned()
.collect()
}
fn write_tuple(&mut self, tuple: RelationTuple) -> Result<(), String> {
if self.store.insert(tuple) {
Ok(())
} else {
Err("Tuple already exists".to_string())
}
}
fn delete_tuple(&mut self, tuple: &RelationTuple) -> Result<(), String> {
if self.store.remove(tuple) {
Ok(())
} else {
Err("Tuple not found".to_string())
}
}
fn all_tuples(&self) -> Vec<RelationTuple> {
self.store.iter().cloned().collect()
}
fn replace_all(&mut self, tuples: Vec<RelationTuple>) {
self.store = tuples.into_iter().collect();
}
}