From a579e90666d49a0f9353319525b1790ad7b26c4a Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Sun, 21 May 2023 11:19:43 +0000 Subject: repos --- src/repos/init.rs | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/repos/mod.rs | 2 + src/repos/repo.rs | 101 ++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 src/repos/init.rs create mode 100644 src/repos/mod.rs create mode 100644 src/repos/repo.rs (limited to 'src') diff --git a/src/repos/init.rs b/src/repos/init.rs new file mode 100644 index 0000000..3254d3e --- /dev/null +++ b/src/repos/init.rs @@ -0,0 +1,183 @@ + +use std::fmt::Debug; +use nostr_sdk::{EventBuilder, Tag, Keys, Event}; +use serde::{Deserialize, Serialize}; + +use crate::{kind::Kind, ngit_tag::{tag_repo, tag_relays, tag_hashtag, tag_group_with_relays, tag_into_event}}; + +/// [`InitializeRepo`] error +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Error serializing or deserializing JSON data + #[error("json error: {0}")] + Json(#[from] serde_json::Error), +} + +impl InitializeRepo { + + pub fn initialize(&self,keys:&Keys) -> Event { + // let keys = Keys::generate(); + EventBuilder::new( + nostr_sdk::Kind::Custom( + match self.root_repo { + None => u64::from(Kind::InitializeRepo), + _ => u64::from(Kind::InitializeBranch), + } + ), + self.as_json(), + &self.generate_tags(), + ) + .to_unsigned_event(keys.public_key()) + .sign(&keys) + .unwrap() + } + + fn generate_tags(&self) -> Vec { + let mut tags = + vec![ + self.maintainers_group.as_ref() + .expect("there always to be a maintainers group when initialising") + .clone(), + tag_hashtag("ngit-event"), + tag_hashtag("ngit-format-0.0.1"), + ]; + if !self.relays.is_empty() { + tags.push( + tag_relays(&self.relays) + ); + } + + match &self.root_repo { + None =>(), + Some(id) => { + tags.push( + tag_repo(id) + ); + tags.push( + tag_into_event( + // its a bit silly / lazy reusing this function just to get the tags formatted with relays when it is not a group + tag_group_with_relays(id, &self.relays) + ) + ); + + } + } + tags + } +} + +/// InitializeRepo +#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] +pub struct InitializeRepo { + /// Name + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + /// Description + #[serde(skip_serializing_if = "Option::is_none")] + pub about: Option, + /// Picture + #[serde(skip_serializing_if = "Option::is_none")] + pub picture: Option, + /// relays + pub relays: Vec, + /// Maintainers Group + #[serde(skip_serializing_if = "Option::is_none")] + pub maintainers_group: Option, + /// Maintainers Group + #[serde(skip_serializing_if = "Option::is_none")] + pub root_repo: Option, +} + +impl Default for InitializeRepo { + fn default() -> Self { + Self::new() + } +} + +impl InitializeRepo { + /// New empty [`InitializeRepo`] + pub fn new() -> Self { + Self { + name: None, + about: None, + picture: None, + relays: vec![], + maintainers_group: None, + root_repo:None, + } + } + + /// Deserialize [`InitializeRepo`] from `JSON` string + pub fn from_json(json: S) -> Result + where + S: Into, + { + Ok(serde_json::from_str(&json.into())?) + } + + /// Serialize [`InitializeRepo`] to `JSON` string + pub fn as_json(&self) -> String { + serde_json::json!(self).to_string() + } + + /// Set name + pub fn name(self, name: S) -> Self + where + S: Into, + { + Self { + name: Some(name.into()), + ..self + } + } + + /// Set about + pub fn about(self, about: S) -> Self + where + S: Into, + { + Self { + about: Some(about.into()), + ..self + } + } + + /// Set picture + pub fn picture(self, picture: S) -> Self + where + S: Into, + { + Self { + picture: Some(picture.into()), + ..self + } + } + + /// Set relays + pub fn relays(mut self, relays: &Vec) -> Self { + for m in relays { + self.relays.push(m.clone()); + } + self + } + + /// Set maintainers_group + pub fn maintainers_group(self, group_ref: Tag) -> Self { + Self { + maintainers_group: Some(group_ref), + ..self + } + } + + /// Set root_repo + pub fn root_repo(self, root_repo: S) -> Self + where + S: Into, + { + Self { + root_repo: Some(root_repo.into()), + ..self + } + } + +} diff --git a/src/repos/mod.rs b/src/repos/mod.rs new file mode 100644 index 0000000..e95cfbd --- /dev/null +++ b/src/repos/mod.rs @@ -0,0 +1,2 @@ +pub mod repo; +pub mod init; diff --git a/src/repos/repo.rs b/src/repos/repo.rs new file mode 100644 index 0000000..0d3001f --- /dev/null +++ b/src/repos/repo.rs @@ -0,0 +1,101 @@ + +use std::{path::PathBuf}; + +use nostr::{EventId, Event, prelude::{Nip19Event, ToBech32}, Tag}; +use nostr_sdk::{Keys}; + +use crate::{groups::{group::{MembershipCollection, StartFinish}}, utils::load_file}; + +use super::init::{InitializeRepo, self}; + +/// [`Repo`] error +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Error processing initialisation Repo content json - incorrect format? + #[error("Repo cannot be initialised from content: {0}")] + InitializeJson(#[from] init::Error), +} + +/// Repo, acts a branch if root_repo is set +pub struct Repo { + pub id: EventId, + pub name:Option, + about:Option, + picture:Option, + pub relays:Vec, + pub maintainers_group:MembershipCollection, + pub events:Vec, + pub root_repo: Option, + hash: String, // hash of event IDs that make up this state +} + +impl Repo { + + pub fn new(init:&InitializeRepo, keys:&Keys) -> Result { + let event = init.initialize(&keys); + Repo::new_from_event(event) + } + + pub fn open(repo_dir_path: &PathBuf) -> Self { + Repo::new_from_json_event( + load_file( + repo_dir_path.join(".ngit/repo.json"), + ) + .expect("repo.json load from file") + ) + .expect("repo.json to produce Repo") + } + + pub fn new_from_json_event(json_string:String) -> Result { + let event = Event::from_json(json_string) + .expect("json_string to be formated as event"); + Repo::new_from_event(event) + } + + pub fn new_from_event(event:Event) -> Result { + match InitializeRepo::from_json(&event.content) { + Err(e) => return Err(Error::InitializeJson(e)), + Ok(g) => { + let start_finish = StartFinish { start: event.created_at, finish: None }; + // add maintainers_group + let mut maintainers_group = MembershipCollection::new(); + match g.maintainers_group { + None => (), + Some(t) => { + maintainers_group.add_group_dates( + Tag::parse(t.into()) + .expect("maintainers_group to parse into Tag"), + start_finish.clone(), + ) + } + } + Ok(Self { + id: event.id, + name: g.name, + about: g.about, + picture: g.picture, + relays: g.relays, + maintainers_group, + events:vec![event], + root_repo:None, + hash: "hash".to_string(), // hash of event IDs that make up this state + }) + } + } + } + + pub fn nevent(&self) -> String { + let e = Nip19Event { + event_id: self.id.clone(), + relays: if self.relays.len() > 1 { + vec![self.relays[0].clone(),self.relays[1].clone()] + } + else if self.relays.len() == 1 { + vec![self.relays[0].clone()] + } + else { vec![] } + }; + e.to_bech32() + .expect("Nip19Event to produce nevent String") + } +} -- cgit v1.2.3