From fda0fdd81caab1ca92eb7ed601058e6c2fdc59f5 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Sun, 21 May 2023 11:18:29 +0000 Subject: helpers and utilities --- src/branch_refs.rs | 274 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 src/branch_refs.rs (limited to 'src/branch_refs.rs') 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 @@ +use std::{path::PathBuf, fs, str::FromStr}; + +use nostr::{Event, Filter, Timestamp, secp256k1::XOnlyPublicKey, EventId}; +use nostr_sdk::blocking::Client; + +use crate::{utils::{load_event, save_event}, kind::Kind, repos::repo::Repo, groups::group::Group, repo_config::RepoConfig}; + + +pub struct BranchRefs { + pub branches: Vec, + pub pull_requests: Vec, + pub merges: Vec, + pub groups: Vec, + repo_dir_path: PathBuf, + pub most_recent_timestamp: Timestamp, +} + +impl BranchRefs { + pub fn new (branch_events: Vec, repo_dir_path: PathBuf) -> Self { + let mut refs = Self { + branches: vec![], + pull_requests: vec![], + merges: vec![], + groups: vec![], + repo_dir_path, + most_recent_timestamp: Timestamp::from(0), + }; + + // add repo first branch in branches vector + refs.update( + load_event(refs.repo_dir_path.join(".ngit/repo.json")) + .expect("repo.json to be present and load as event") + ); + + //load locally + for dir_name in [ + "groups", + "branches", + "merges", + "prs", + ] { + let dir_path = refs.repo_dir_path.join(".ngit").join(&dir_name); + if dir_path.exists() { + let dir = fs::read_dir(&dir_path) + .expect("read_dir to produce ReadDir from a path that exists"); + for entry in dir { + let path = entry + .expect("DirEntry to return from ReadDir") + .path(); + // load each BranchRef event in .ngit and call update + refs.update( + load_event(path) + .expect("every file in .ngit paths is a valid json event") + ); + } + } + else { + panic!("expected dir to exist in branch_refs"); + } + } + refs.updates(branch_events); + refs + } + + pub fn updates (&mut self, branch_events: Vec) { + for event in branch_events.clone().into_iter() { + self.update(event); + } + let mut repo_config = RepoConfig::open(&self.repo_dir_path); + repo_config.set_last_branch_ref_update_time(self.most_recent_timestamp.clone()); + } + + pub fn update (&mut self, event: Event) { + let event_to_store = event.clone(); + // /// check event is for repo + // fn event_is_for_repo(event: &Event,branch_refs: &BranchRefs) -> bool { + // match event.tags.iter().find(|tag| tag_is_repo(tag)) { + // None => false, + // Some(tag) => { + // match branch_refs.branches.get(0) { + // None => true, // current repo unknown + // Some(b) => tag_extract_value(tag) == b.id.to_string(), + // } + // }, + // } + // } + + // update most_recent_timestamp + if event.created_at > self.most_recent_timestamp { + self.most_recent_timestamp = event.created_at.clone(); + } + + // add events to vectors + let dir_name = match Kind::from(event.clone().kind.as_u64()) { + Kind::InitializeRepo => { + // if !self.branches.iter().any(|e| e.id == event.id) + // && event_is_for_repo(&event, &self) { + if !self.branches.iter().any(|e| e.id == event.id) { + self.branches.push(event); + Some("branches") + } + else { None } + }, + Kind::InitializeBranch => { + // if !self.branches.iter().any(|e| e.id == event.id) + // && event_is_for_repo(&event, &self) { + if !self.branches.iter().any(|e| e.id == event.id) { + self.branches.push(event); + Some("branches") + } + else { None } + }, + Kind::PullRequest => { + // if !self.pull_requests.iter().any(|e| e.id == event.id) + // && event_is_for_repo(&event, &self) { + if !self.pull_requests.iter().any(|e| e.id == event.id) { + self.pull_requests.push(event); + Some("prs") + } + else { None } + } + Kind::Merge => { + // if !self.merges.iter().any(|e| e.id == event.id) + // && event_is_for_repo(&event, &self) { + if !self.merges.iter().any(|e| e.id == event.id) { + self.merges.push(event); + Some("merges") + } + else { None } + }, + Kind::InitializeGroup => { + if !self.groups.iter().any(|e| e.id == event.id) { + self.groups.push(event); + Some("groups") + } + else { None } + }, + _ => None, + }; + + // store events in .ngit directory + match dir_name { + Some(dir_name) => { + let path = self.repo_dir_path.join(".ngit").join(format!("{}/{}.json",dir_name, event_to_store.id)); + if !path.exists() { + save_event(&path, &event_to_store) + .expect(format!("save_event will store BranchRefs event in {}",&path.to_string_lossy()).as_str()); + } + }, + None => (), + } + } + + fn branch_event(&self, branch_id: Option<&String>) -> Event { + match branch_id { + None => self.branches[0].clone(), + Some(branch_id) => self.branches.iter().find(|b| b.id.to_string() == *branch_id) + .expect("BranchRefs.branch_event() will always be called with a branch_id from a branch in its cache") + .clone(), + } + } + + pub fn branch_as_repo(&self, branch_id: Option<&String>) -> Repo { + Repo::new_from_event(self.branch_event(branch_id)) + .expect("event in BranchRefs.branches to produce Repo") + } + + /// assumes the branch_id is in cachse + pub fn maintainers_group_id(&self, branch_id: Option<&String>) -> EventId { + self.branch_as_repo(branch_id) + .maintainers_group.get_first_active_group() + .expect("a repo will always have an active maintainers group") + .clone() + } + + /// assumes the branch_id is in cache. returns None if maintainers group event cannot be found. + pub fn maintainers_group(&self, branch_or_group_id: Option<&String>) -> Option { + match self.groups.iter().find(|g| + // for branch id + g.id == self.maintainers_group_id(branch_or_group_id) + // for group id + || match branch_or_group_id { + None => false, + Some(id) => g.id == EventId::from_str(id).expect("id to be valid event id"), + }, + ) { + None => None, + Some(event) => Some( + Group::new_from_event(event.clone()) + .expect("group stored in BranchRefs.groups will always produce Group") + ), + } + } + + /// returns None if maintainers group event cannot be found + pub fn is_authorized(&self, branch_id: Option<&String>, pubkey: &XOnlyPublicKey) -> Option { + match self.maintainers_group(branch_id) { + None => None, + Some(group) => Some( + group.is_member(pubkey) + // TODO - add support for nested groups so 'is_member' checks for indirect membership + // for it will just be members of the branch group or maintainers group + || + match self.maintainers_group(None) { + None => false, + Some(group) => group.is_member(pubkey), + } + ), + } + } + + pub fn group_ids_for_branches_without_cached_groups(&self) -> Vec { + self.branches.iter() + .map(|b| + self.maintainers_group_id(Some(&b.id.to_string())) + .clone() + ) + .filter(|id|!self.groups.iter().any(|e|e.id == *id)) + .collect() + + } +} + +pub fn get_branch_refs (repo: &Repo, client: &Client, repo_dir_path: &PathBuf) -> BranchRefs { + + let mut refs = BranchRefs::new(vec![],repo_dir_path.clone()); + + let repo_config = RepoConfig::open(repo_dir_path); + + // filter for branches, PRs and Merges + let mut tag_filter = Filter::new() + .event(repo.id) + .kinds(vec![ + Kind::InitializeBranch.into_sdk_custom_kind(), + Kind::PullRequest.into_sdk_custom_kind(), + Kind::Merge.into_sdk_custom_kind(), + ]); + match repo_config.last_branch_ref_update_time() { + None => (), + Some(timestamp) => { + tag_filter = tag_filter.since(timestamp.clone()); + } + }; + + let branch_events: Vec = client.get_events_of( + vec![ + // branch maintainer groups + Filter::new().ids(refs.group_ids_for_branches_without_cached_groups()), + tag_filter, + ], + None, + ) + .expect("get_events_of to not return an error"); + + refs.updates(branch_events); + // refs.merged_branches_ids.push(repo.id.to_string()); + + // for event in refs.merges.iter() { + // match &event.tags.iter().find(|t|tag_is_branch(t)) { + // None => {}, + // Some(t) => { + // match &refs.maintainers_group { + // None => (), + // Some(g) => { + // if g.is_member(&event.pubkey) { + // refs.merged_branches_ids.push(tag_extract_value(t)); + // } + // } + // } + // } + // } + // } + refs +} -- cgit v1.2.3