upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/branch_refs.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/branch_refs.rs')
-rw-r--r--src/branch_refs.rs274
1 files changed, 274 insertions, 0 deletions
diff --git a/src/branch_refs.rs b/src/branch_refs.rs
new file mode 100644
index 0000000..d6f9613
--- /dev/null
+++ b/src/branch_refs.rs
@@ -0,0 +1,274 @@
1use std::{path::PathBuf, fs, str::FromStr};
2
3use nostr::{Event, Filter, Timestamp, secp256k1::XOnlyPublicKey, EventId};
4use nostr_sdk::blocking::Client;
5
6use crate::{utils::{load_event, save_event}, kind::Kind, repos::repo::Repo, groups::group::Group, repo_config::RepoConfig};
7
8
9pub struct BranchRefs {
10 pub branches: Vec<Event>,
11 pub pull_requests: Vec<Event>,
12 pub merges: Vec<Event>,
13 pub groups: Vec<Event>,
14 repo_dir_path: PathBuf,
15 pub most_recent_timestamp: Timestamp,
16}
17
18impl BranchRefs {
19 pub fn new (branch_events: Vec<Event>, repo_dir_path: PathBuf) -> Self {
20 let mut refs = Self {
21 branches: vec![],
22 pull_requests: vec![],
23 merges: vec![],
24 groups: vec![],
25 repo_dir_path,
26 most_recent_timestamp: Timestamp::from(0),
27 };
28
29 // add repo first branch in branches vector
30 refs.update(
31 load_event(refs.repo_dir_path.join(".ngit/repo.json"))
32 .expect("repo.json to be present and load as event")
33 );
34
35 //load locally
36 for dir_name in [
37 "groups",
38 "branches",
39 "merges",
40 "prs",
41 ] {
42 let dir_path = refs.repo_dir_path.join(".ngit").join(&dir_name);
43 if dir_path.exists() {
44 let dir = fs::read_dir(&dir_path)
45 .expect("read_dir to produce ReadDir from a path that exists");
46 for entry in dir {
47 let path = entry
48 .expect("DirEntry to return from ReadDir")
49 .path();
50 // load each BranchRef event in .ngit and call update
51 refs.update(
52 load_event(path)
53 .expect("every file in .ngit paths is a valid json event")
54 );
55 }
56 }
57 else {
58 panic!("expected dir to exist in branch_refs");
59 }
60 }
61 refs.updates(branch_events);
62 refs
63 }
64
65 pub fn updates (&mut self, branch_events: Vec<Event>) {
66 for event in branch_events.clone().into_iter() {
67 self.update(event);
68 }
69 let mut repo_config = RepoConfig::open(&self.repo_dir_path);
70 repo_config.set_last_branch_ref_update_time(self.most_recent_timestamp.clone());
71 }
72
73 pub fn update (&mut self, event: Event) {
74 let event_to_store = event.clone();
75 // /// check event is for repo
76 // fn event_is_for_repo(event: &Event,branch_refs: &BranchRefs) -> bool {
77 // match event.tags.iter().find(|tag| tag_is_repo(tag)) {
78 // None => false,
79 // Some(tag) => {
80 // match branch_refs.branches.get(0) {
81 // None => true, // current repo unknown
82 // Some(b) => tag_extract_value(tag) == b.id.to_string(),
83 // }
84 // },
85 // }
86 // }
87
88 // update most_recent_timestamp
89 if event.created_at > self.most_recent_timestamp {
90 self.most_recent_timestamp = event.created_at.clone();
91 }
92
93 // add events to vectors
94 let dir_name = match Kind::from(event.clone().kind.as_u64()) {
95 Kind::InitializeRepo => {
96 // if !self.branches.iter().any(|e| e.id == event.id)
97 // && event_is_for_repo(&event, &self) {
98 if !self.branches.iter().any(|e| e.id == event.id) {
99 self.branches.push(event);
100 Some("branches")
101 }
102 else { None }
103 },
104 Kind::InitializeBranch => {
105 // if !self.branches.iter().any(|e| e.id == event.id)
106 // && event_is_for_repo(&event, &self) {
107 if !self.branches.iter().any(|e| e.id == event.id) {
108 self.branches.push(event);
109 Some("branches")
110 }
111 else { None }
112 },
113 Kind::PullRequest => {
114 // if !self.pull_requests.iter().any(|e| e.id == event.id)
115 // && event_is_for_repo(&event, &self) {
116 if !self.pull_requests.iter().any(|e| e.id == event.id) {
117 self.pull_requests.push(event);
118 Some("prs")
119 }
120 else { None }
121 }
122 Kind::Merge => {
123 // if !self.merges.iter().any(|e| e.id == event.id)
124 // && event_is_for_repo(&event, &self) {
125 if !self.merges.iter().any(|e| e.id == event.id) {
126 self.merges.push(event);
127 Some("merges")
128 }
129 else { None }
130 },
131 Kind::InitializeGroup => {
132 if !self.groups.iter().any(|e| e.id == event.id) {
133 self.groups.push(event);
134 Some("groups")
135 }
136 else { None }
137 },
138 _ => None,
139 };
140
141 // store events in .ngit directory
142 match dir_name {
143 Some(dir_name) => {
144 let path = self.repo_dir_path.join(".ngit").join(format!("{}/{}.json",dir_name, event_to_store.id));
145 if !path.exists() {
146 save_event(&path, &event_to_store)
147 .expect(format!("save_event will store BranchRefs event in {}",&path.to_string_lossy()).as_str());
148 }
149 },
150 None => (),
151 }
152 }
153
154 fn branch_event(&self, branch_id: Option<&String>) -> Event {
155 match branch_id {
156 None => self.branches[0].clone(),
157 Some(branch_id) => self.branches.iter().find(|b| b.id.to_string() == *branch_id)
158 .expect("BranchRefs.branch_event() will always be called with a branch_id from a branch in its cache")
159 .clone(),
160 }
161 }
162
163 pub fn branch_as_repo(&self, branch_id: Option<&String>) -> Repo {
164 Repo::new_from_event(self.branch_event(branch_id))
165 .expect("event in BranchRefs.branches to produce Repo")
166 }
167
168 /// assumes the branch_id is in cachse
169 pub fn maintainers_group_id(&self, branch_id: Option<&String>) -> EventId {
170 self.branch_as_repo(branch_id)
171 .maintainers_group.get_first_active_group()
172 .expect("a repo will always have an active maintainers group")
173 .clone()
174 }
175
176 /// assumes the branch_id is in cache. returns None if maintainers group event cannot be found.
177 pub fn maintainers_group(&self, branch_or_group_id: Option<&String>) -> Option<Group> {
178 match self.groups.iter().find(|g|
179 // for branch id
180 g.id == self.maintainers_group_id(branch_or_group_id)
181 // for group id
182 || match branch_or_group_id {
183 None => false,
184 Some(id) => g.id == EventId::from_str(id).expect("id to be valid event id"),
185 },
186 ) {
187 None => None,
188 Some(event) => Some(
189 Group::new_from_event(event.clone())
190 .expect("group stored in BranchRefs.groups will always produce Group")
191 ),
192 }
193 }
194
195 /// returns None if maintainers group event cannot be found
196 pub fn is_authorized(&self, branch_id: Option<&String>, pubkey: &XOnlyPublicKey) -> Option<bool> {
197 match self.maintainers_group(branch_id) {
198 None => None,
199 Some(group) => Some(
200 group.is_member(pubkey)
201 // TODO - add support for nested groups so 'is_member' checks for indirect membership
202 // for it will just be members of the branch group or maintainers group
203 ||
204 match self.maintainers_group(None) {
205 None => false,
206 Some(group) => group.is_member(pubkey),
207 }
208 ),
209 }
210 }
211
212 pub fn group_ids_for_branches_without_cached_groups(&self) -> Vec<EventId> {
213 self.branches.iter()
214 .map(|b|
215 self.maintainers_group_id(Some(&b.id.to_string()))
216 .clone()
217 )
218 .filter(|id|!self.groups.iter().any(|e|e.id == *id))
219 .collect()
220
221 }
222}
223
224pub fn get_branch_refs (repo: &Repo, client: &Client, repo_dir_path: &PathBuf) -> BranchRefs {
225
226 let mut refs = BranchRefs::new(vec![],repo_dir_path.clone());
227
228 let repo_config = RepoConfig::open(repo_dir_path);
229
230 // filter for branches, PRs and Merges
231 let mut tag_filter = Filter::new()
232 .event(repo.id)
233 .kinds(vec![
234 Kind::InitializeBranch.into_sdk_custom_kind(),
235 Kind::PullRequest.into_sdk_custom_kind(),
236 Kind::Merge.into_sdk_custom_kind(),
237 ]);
238 match repo_config.last_branch_ref_update_time() {
239 None => (),
240 Some(timestamp) => {
241 tag_filter = tag_filter.since(timestamp.clone());
242 }
243 };
244
245 let branch_events: Vec<Event> = client.get_events_of(
246 vec![
247 // branch maintainer groups
248 Filter::new().ids(refs.group_ids_for_branches_without_cached_groups()),
249 tag_filter,
250 ],
251 None,
252 )
253 .expect("get_events_of to not return an error");
254
255 refs.updates(branch_events);
256 // refs.merged_branches_ids.push(repo.id.to_string());
257
258 // for event in refs.merges.iter() {
259 // match &event.tags.iter().find(|t|tag_is_branch(t)) {
260 // None => {},
261 // Some(t) => {
262 // match &refs.maintainers_group {
263 // None => (),
264 // Some(g) => {
265 // if g.is_member(&event.pubkey) {
266 // refs.merged_branches_ids.push(tag_extract_value(t));
267 // }
268 // }
269 // }
270 // }
271 // }
272 // }
273 refs
274}