upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/groups/group.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/groups/group.rs')
-rw-r--r--src/groups/group.rs357
1 files changed, 357 insertions, 0 deletions
diff --git a/src/groups/group.rs b/src/groups/group.rs
new file mode 100644
index 0000000..d7f2fbf
--- /dev/null
+++ b/src/groups/group.rs
@@ -0,0 +1,357 @@
1use std::{str::FromStr, path::PathBuf};
2
3use nostr::{EventId, secp256k1::XOnlyPublicKey, prelude::UncheckedUrl, Tag, Event};
4use nostr_sdk::{Timestamp, Keys};
5
6use crate::{utils::load_event, ngit_tag::{tag_extract_value_as_event_id, tag_extract_relays, tag_is_group, tag_group_with_relays}};
7
8use super::{init::{InitializeGroup, self}};
9
10/// [`Group`] error
11#[derive(Debug, thiserror::Error)]
12pub enum Error {
13 /// Error processing initialisation group content json - incorrect format?
14 #[error("group cannot be initialised from content: {0}")]
15 InitializeJson(#[from] init::Error),
16 /// Error group is not avialable locally in .ngit
17 #[error("group is not available in .ngit/groups/{0}.json")]
18 GroupJsonNotAvailable(String),
19}
20
21#[derive(Eq, PartialEq, Clone)]
22pub struct StartFinish {
23 pub start:Timestamp,
24 pub finish: Option<Timestamp>,
25}
26
27struct MembershipDetails {
28 pub id:EventId,
29 pub dates: Vec<StartFinish>,
30 pub relays: Vec<UncheckedUrl>,
31}
32pub struct MembershipCollection {
33 collection:Vec<MembershipDetails>,
34}
35impl MembershipCollection {
36 pub fn new() -> Self {
37 Self {
38 collection: vec![],
39 }
40 }
41 pub fn add_group_dates(
42 &mut self,
43 group_tag:Tag,
44 start_finish:StartFinish,
45 ) {
46
47 if !tag_is_group(&group_tag) {
48 panic!("tag supplied to add_group_dates isn't a group tag");
49 }
50
51 match self.collection.iter_mut().find(
52 |g| tag_extract_value_as_event_id(&group_tag).eq(&g.id)
53 ) {
54 None => {
55 self.collection.push(
56 MembershipDetails {
57 id: tag_extract_value_as_event_id(&group_tag),
58 dates: vec![
59 start_finish
60 ],
61 relays: tag_extract_relays(&group_tag),
62 }
63 );
64 },
65 Some(group_dates_relays) => {
66 match group_dates_relays.dates.iter().find(
67 |d| start_finish.eq(&d)
68 ) {
69 None => group_dates_relays.dates.push(start_finish),
70 Some(_) => (),
71 }
72 }
73 }
74 }
75
76 pub fn get_first_active_group(&self) -> Option<&EventId> {
77 let a = self.get_active_groups();
78 if a.is_empty() { None }
79 else { Some(a[0]) }
80 }
81
82 pub fn get_active_groups(&self) -> Vec<&EventId> {
83 let mut active = vec![];
84 for m in &self.collection {
85 if m.dates.iter().any(|sf| sf.finish.is_none()) {
86 active.push(&m.id);
87 }
88 }
89 active
90 }
91}
92
93pub struct PubKeyDates {
94 pubkey:XOnlyPublicKey,
95 dates: Vec<StartFinish>,
96}
97
98pub struct Group {
99 pub id: EventId,
100 name:Option<String>,
101 about:Option<String>,
102 picture:Option<String>,
103 pub relays:Vec<String>,
104 direct_members: Vec<PubKeyDates>,
105 member_groups:MembershipCollection,
106 indirect_member_groups:MembershipCollection,
107 admin_group:MembershipCollection,
108 indirect_admin_groups:MembershipCollection,
109 pub events:Vec<Event>,
110 hash: String, // hash of event IDs that make up this state
111}
112
113impl Group {
114
115 pub fn new(init:&InitializeGroup, keys:&Keys) -> Result<Self,Error> {
116 let event = init.initialize(&keys);
117 Group::new_from_event(event)
118 }
119
120 pub fn new_from_json_event(json_string:String) -> Result<Self,Error> {
121 let event = Event::from_json(json_string)
122 .expect("json_string to be formated as event");
123 Group::new_from_event(event)
124 }
125
126 pub fn open (group_id:String, repo_dir_path:&PathBuf) -> Result<Self,Error> {
127 let path = repo_dir_path.join(
128 format!(".ngit/groups/{}.json",group_id)
129 );
130 if path.exists() {
131 Ok(
132 Group::new_from_event(
133 load_event(path)
134 .expect("group event in json to be well formatted as a group event"),
135 )
136 .expect("file content at path to be a well formated group event")
137 )
138 }
139 else {
140 Err(Error::GroupJsonNotAvailable(group_id))
141 }
142 }
143
144 pub fn new_from_event(event:Event) -> Result<Self,Error> {
145 match InitializeGroup::from_json(&event.content) {
146 Err(e) => return Err(Error::InitializeJson(e)),
147 Ok(g) => {
148 let start_finish = StartFinish { start: event.created_at, finish: None };
149 let mut direct_members: Vec<PubKeyDates> = vec![];
150 // add direct_members
151 for m in g.direct_members {
152 let key = XOnlyPublicKey::from_str(m.as_str());
153 match key {
154 Ok(k) => direct_members.push(
155 PubKeyDates {
156 pubkey: k,
157 dates: vec![
158 start_finish.clone(),
159 ]
160 },
161 ),
162 // could add pubkey to an invalid vector and report on it?
163 Err(_) => (),
164 }
165 }
166 // add member groups
167 // let mut member_groups: Vec<GroupDatesRelays> = vec![];
168 let mut member_groups = MembershipCollection::new();
169 for m in g.member_groups {
170 // let event_id_relay = EventIdRelays::from_tag(m);
171 // member_groups.push(GroupDatesRelays {
172 // id: event_id_relay.id,
173 // dates: vec![
174 // StartFinish { start: event.created_at, finish: None }
175 // ],
176 // relays: match event_id_relay.relay {
177 // None => vec![],
178 // Some(r) => vec![r],
179 // }
180 // });
181 // add_group_dates_to_vector(
182 // &mut member_groups,
183 // m,
184 // StartFinish { start: event.created_at, finish: None },
185 // )
186 member_groups.add_group_dates(
187 m,
188 start_finish.clone(),
189 )
190 }
191 // add admin group
192 // let admin_group = match g.admin {
193 // None => vec![],
194 // Some(a) => match EventId::from_str(a.as_str()) {
195 // Ok(id) => vec![
196 // GroupDatesRelays {
197 // id,
198 // dates: vec![StartFinish {
199 // start: event.created_at.clone(),
200 // finish: None,
201 // }]
202 // }
203 // ],
204 // // could report on it?
205 // Err(_) => vec![],
206 // }
207 // };
208 let mut admin_group = MembershipCollection::new();
209 // let admin_group = vec![];
210 match g.admin {
211 None => (),
212 Some(t) => {
213 admin_group.add_group_dates(
214 t,
215 start_finish.clone(),
216 )
217 }
218 }
219 Ok(Self {
220 id: event.id,
221 name: g.name,
222 about: g.about,
223 picture: g.picture,
224 relays: g.relays,
225 direct_members,
226 admin_group,
227 member_groups,
228 indirect_member_groups: MembershipCollection::new(),
229 indirect_admin_groups: MembershipCollection::new(),
230 events:vec![event],
231 hash: "hash".to_string(), // hash of event IDs that make up this state
232 })
233 }
234 }
235 }
236
237 fn load_recurring_sub_groups() {
238
239 }
240
241 pub fn get_ref(&self) -> Tag {
242 tag_group_with_relays(
243 &self.id.to_string(),
244 &self.relays,
245 )
246 }
247
248 // fn add_member(&self,) -> Self;
249 // fn init(&self,keys:Keys) -> Event;
250 // fn remove_member() -> Self;
251 // fn set_admin(&self) -> Self;
252 // fn set_name(&self) -> Self;
253 // fn set_about(&self) -> Self;
254 // fn set_picture(&self) -> Self;
255
256 // use enums instead of having so many functions? then use a vector to store all the changes so they can be made in one event?
257
258 // fn new(
259 // &self,
260 // direct_members:Vec<String>,
261 // sub_groups:String,
262 // relays:Vec<String>,
263 // name:String,
264 // ) -> Self {
265 // // create initialation event
266 // // EventBuilder::new(
267 // // 100,
268
269 // // )
270 // self
271 // }
272
273 pub fn members(&self) -> Vec<&XOnlyPublicKey> {
274 let mut pubkeys = vec![];
275 for m in &self.direct_members {
276 pubkeys.push(&m.pubkey);
277 }
278 pubkeys
279 }
280
281 pub fn is_member(&self, pubkey: &XOnlyPublicKey) -> bool {
282 self.members().iter().any(|m| *pubkey == **m)
283 }
284 // pub fn admins(&self) -> Vec<&String> { get_el(&self.admins) }
285 // pub fn voters(&self) -> Vec<&String> { get_el(&self.voters) }
286 // pub fn members(&self) -> Vec<&String> { get_el(&self.members) }
287 // pub fn is_admin(&self, pubkey:&String) -> bool { is_el(&self.admins, &pubkey) }
288 // pub fn is_voter(&self, pubkey:&String) -> bool { is_el(&self.voters, &pubkey) }
289 // pub fn is_member(&self, pubkey:&String) -> bool { is_el(&self.members, &pubkey) }
290 // pub fn get_admins_at<'a>(&'a self, timestamp: &'a Timestamp) -> Vec<&String> { get_el_at(&self.admins,&timestamp) }
291 // pub fn get_voters_at<'a>(&'a self, timestamp: &'a Timestamp) -> Vec<&String> { get_el_at(&self.voters,&timestamp) }
292 // pub fn get_members_at<'a>(&'a self, timestamp: &'a Timestamp) -> Vec<&String> { get_el_at(&self.members,&timestamp) }
293 // pub fn was_admin_at(&self, pubkey:&String, timestamp: &Timestamp) -> bool { was_el_at(&self.admins, pubkey, &timestamp) }
294 // pub fn was_voter_at(&self, pubkey:&String, timestamp: &Timestamp) -> bool { was_el_at(&self.voters, pubkey, &timestamp) }
295 // pub fn was_members_at(&self, pubkey:&String, timestamp: &Timestamp) -> bool { was_el_at(&self.members, pubkey, &timestamp) }
296}
297
298// fn get_el(el:&Vec<PubKeyDates>) -> Vec<&String> {
299// let mut current: Vec<&String> = vec![];
300// for m in el {
301// if m.dates.last().unwrap().finish.is_none() {
302// current.push(&m.pubkey)
303// }
304// }
305// current
306// }
307// fn is_el(el:&Vec<PubKeyDates>, pubkey:&String) -> bool {
308// el
309// .iter()
310// .any(
311// |m|
312// &m.pubkey == pubkey
313// && m.dates.last().unwrap().finish.is_none()
314// )
315// }
316// fn get_el_at<'a>(el:&'a Vec<PubKeyDates>, timestamp: &'a Timestamp) -> Vec<&'a String> {
317// let mut el_at_timestamp: Vec<&String> = vec![];
318// for m in el {
319// if m.dates
320// .iter()
321// .any(
322// |d|
323// &d.start < &timestamp
324// // && match &d.finish {
325// // None => true,
326// // _ => &d.finish.unwrap() > &timestamp
327// // }
328// && (
329// d.finish.is_none()
330// || &d.finish.unwrap() > &timestamp
331// )
332// ) {
333// el_at_timestamp.push(&m.pubkey)
334// }
335// }
336// el_at_timestamp
337// }
338// fn was_el_at(el:&Vec<PubKeyDates>, pubkey:&String,timestamp:&Timestamp) -> bool {
339// // PublicKey::try_from_hex_string(pubkey);
340// el
341// .iter()
342// .any(
343// |m|
344// &m.pubkey == pubkey
345// && m.dates
346// .iter()
347// .any(
348// |d|
349// &d.start < &timestamp
350// && (
351// d.finish.is_none()
352// || &d.finish.unwrap() > &timestamp
353// )
354// )
355// )
356// }
357