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