Skip to main content

kcode_k1_groups_domain/
lib.rs

1pub use kcode_k1_invites::UserId;
2pub use kcode_k1_txn_ordering::TxId;
3
4#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
5pub struct GroupId(TxId);
6
7#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub enum SentinelGroup {
9    AllUsers,
10    AllModels,
11    LocalModels,
12}
13
14pub const ALL_USERS: GroupId = GroupId::new(TxId::from_bytes([
15    255, 75, 49, 71, 82, 80, 0, 0, 0, 0, 0, 1,
16]));
17pub const ALL_MODELS: GroupId = GroupId::new(TxId::from_bytes([
18    255, 75, 49, 71, 82, 80, 0, 0, 0, 0, 0, 2,
19]));
20pub const LOCAL_MODELS: GroupId = GroupId::new(TxId::from_bytes([
21    255, 75, 49, 71, 82, 80, 0, 0, 0, 0, 0, 3,
22]));
23
24impl GroupId {
25    pub const fn new(txid: TxId) -> Self {
26        Self(txid)
27    }
28
29    pub const fn txid(self) -> TxId {
30        self.0
31    }
32
33    pub const fn sentinel(self) -> Option<SentinelGroup> {
34        match self {
35            ALL_USERS => Some(SentinelGroup::AllUsers),
36            ALL_MODELS => Some(SentinelGroup::AllModels),
37            LOCAL_MODELS => Some(SentinelGroup::LocalModels),
38            _ => None,
39        }
40    }
41}
42
43#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
44pub struct ModelId([u8; 32]);
45
46impl ModelId {
47    pub const fn from_bytes(bytes: [u8; 32]) -> Self {
48        Self(bytes)
49    }
50
51    pub const fn as_bytes(&self) -> &[u8; 32] {
52        &self.0
53    }
54
55    pub const fn into_bytes(self) -> [u8; 32] {
56        self.0
57    }
58}
59
60#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
61pub struct GroupName(String);
62
63impl GroupName {
64    pub fn new(value: String) -> Result<Self, String> {
65        if !(1..=128).contains(&value.len()) {
66            return Err("group name must be 1 through 128 UTF-8 bytes".to_owned());
67        }
68        if value.chars().any(char::is_control) {
69            return Err("group name must not contain control characters".to_owned());
70        }
71        if !value.chars().any(|character| !character.is_whitespace()) {
72            return Err("group name must contain a non-whitespace character".to_owned());
73        }
74        Ok(Self(value))
75    }
76
77    pub fn as_str(&self) -> &str {
78        &self.0
79    }
80
81    pub fn into_string(self) -> String {
82        self.0
83    }
84}
85
86#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
87pub enum GroupRole {
88    User,
89    Admin,
90    Owner,
91}
92
93#[derive(Clone, Debug, Eq, PartialEq)]
94pub struct GroupUser {
95    user_id: UserId,
96    role: GroupRole,
97}
98
99impl GroupUser {
100    pub fn user_id(&self) -> UserId {
101        self.user_id
102    }
103
104    pub fn role(&self) -> GroupRole {
105        self.role
106    }
107}
108
109#[derive(Clone, Debug, Eq, PartialEq)]
110pub struct GroupRevision {
111    group_id: GroupId,
112    txid: TxId,
113}
114
115impl GroupRevision {
116    pub fn group_id(&self) -> GroupId {
117        self.group_id
118    }
119
120    pub fn txid(&self) -> TxId {
121        self.txid
122    }
123}
124
125#[derive(Clone, Debug, Eq, PartialEq)]
126pub struct Group {
127    id: GroupId,
128    name: GroupName,
129    revision: GroupRevision,
130    users: Vec<GroupUser>,
131    models: Vec<ModelId>,
132}
133
134impl Group {
135    pub fn id(&self) -> GroupId {
136        self.id
137    }
138
139    pub fn name(&self) -> &GroupName {
140        &self.name
141    }
142
143    pub fn revision(&self) -> &GroupRevision {
144        &self.revision
145    }
146
147    pub fn users(&self) -> &[GroupUser] {
148        &self.users
149    }
150
151    pub fn models(&self) -> &[ModelId] {
152        &self.models
153    }
154}
155
156#[derive(Clone, Debug, Eq, PartialEq)]
157pub struct GroupMemberships {
158    revision: Option<TxId>,
159    user_groups: Vec<GroupId>,
160    model_groups: Vec<GroupId>,
161    shared_groups: Vec<GroupId>,
162}
163
164impl GroupMemberships {
165    pub fn revision(&self) -> Option<TxId> {
166        self.revision
167    }
168
169    pub fn user_groups(&self) -> &[GroupId] {
170        &self.user_groups
171    }
172
173    pub fn model_groups(&self) -> &[GroupId] {
174        &self.model_groups
175    }
176
177    pub fn shared_groups(&self) -> &[GroupId] {
178        &self.shared_groups
179    }
180}
181
182#[derive(Clone, Debug, Eq, PartialEq)]
183pub enum GroupAction {
184    Create {
185        owner: UserId,
186        name: GroupName,
187    },
188    Rename {
189        group: GroupId,
190        actor: UserId,
191        name: GroupName,
192    },
193    SetUserRole {
194        group: GroupId,
195        actor: UserId,
196        user: UserId,
197        role: Option<GroupRole>,
198    },
199    SetModelMembership {
200        group: GroupId,
201        actor: UserId,
202        model: ModelId,
203        present: bool,
204    },
205}
206
207#[derive(Clone, Debug, Eq, PartialEq)]
208pub enum ApplyOutcome {
209    Applied(GroupRevision),
210    Unchanged(GroupRevision),
211    Rejected(String),
212}
213
214pub mod projection_values {
215    use super::{
216        Group, GroupId, GroupMemberships, GroupName, GroupRevision, GroupRole, GroupUser, ModelId,
217        TxId, UserId,
218    };
219
220    pub fn group_user(user_id: UserId, role: GroupRole) -> GroupUser {
221        GroupUser { user_id, role }
222    }
223
224    pub fn group_revision(group_id: GroupId, txid: TxId) -> GroupRevision {
225        GroupRevision { group_id, txid }
226    }
227
228    pub fn group(
229        id: GroupId,
230        name: GroupName,
231        revision: GroupRevision,
232        users: Vec<GroupUser>,
233        models: Vec<ModelId>,
234    ) -> Group {
235        Group {
236            id,
237            name,
238            revision,
239            users,
240            models,
241        }
242    }
243
244    pub fn group_memberships(
245        revision: Option<TxId>,
246        user_groups: Vec<GroupId>,
247        model_groups: Vec<GroupId>,
248        shared_groups: Vec<GroupId>,
249    ) -> GroupMemberships {
250        GroupMemberships {
251            revision,
252            user_groups,
253            model_groups,
254            shared_groups,
255        }
256    }
257}