upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/lib/repo_ref.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-08-07 17:52:22 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2025-08-07 17:52:22 +0100
commit92c2362a9bed1bc1f256e7948e087c4102b7c4f9 (patch)
tree700bf840ba52a8bd576afcfbe532a669f104dfcb /src/lib/repo_ref.rs
parent8724af191f520a822214109f75a1851856c74fd2 (diff)
parentfa7adf840ac2d78defee398a61b60888f615622a (diff)
Merge branch 'add-prs-to-ngit-send'
Diffstat (limited to 'src/lib/repo_ref.rs')
-rw-r--r--src/lib/repo_ref.rs87
1 files changed, 82 insertions, 5 deletions
diff --git a/src/lib/repo_ref.rs b/src/lib/repo_ref.rs
index bca4a3b..b2bd381 100644
--- a/src/lib/repo_ref.rs
+++ b/src/lib/repo_ref.rs
@@ -40,6 +40,7 @@ pub struct RepoRef {
40 pub web: Vec<String>, 40 pub web: Vec<String>,
41 pub relays: Vec<RelayUrl>, 41 pub relays: Vec<RelayUrl>,
42 pub blossoms: Vec<Url>, 42 pub blossoms: Vec<Url>,
43 pub hashtags: Vec<String>,
43 pub maintainers: Vec<PublicKey>, 44 pub maintainers: Vec<PublicKey>,
44 pub trusted_maintainer: PublicKey, 45 pub trusted_maintainer: PublicKey,
45 // set to None if not known 46 // set to None if not known
@@ -71,6 +72,7 @@ impl TryFrom<(nostr::Event, Option<PublicKey>)> for RepoRef {
71 web: Vec::new(), 72 web: Vec::new(),
72 relays: Vec::new(), 73 relays: Vec::new(),
73 blossoms: Vec::new(), 74 blossoms: Vec::new(),
75 hashtags: Vec::new(),
74 maintainers: Vec::new(), 76 maintainers: Vec::new(),
75 trusted_maintainer: trusted_maintainer.unwrap_or(event.pubkey), 77 trusted_maintainer: trusted_maintainer.unwrap_or(event.pubkey),
76 maintainers_without_annoucnement: None, 78 maintainers_without_annoucnement: None,
@@ -118,6 +120,7 @@ impl TryFrom<(nostr::Event, Option<PublicKey>)> for RepoRef {
118 } 120 }
119 } 121 }
120 } 122 }
123 [t, hashtag, ..] if t == "t" => r.hashtags.push(hashtag.clone()),
121 [t, blossoms @ ..] if t == "blossoms" => { 124 [t, blossoms @ ..] if t == "blossoms" => {
122 for b in blossoms { 125 for b in blossoms {
123 if let Ok(b) = Url::parse(b) { 126 if let Ok(b) = Url::parse(b) {
@@ -217,6 +220,15 @@ impl RepoRef {
217 vec![format!("git repository: {}", self.name.clone())], 220 vec![format!("git repository: {}", self.name.clone())],
218 ), 221 ),
219 ], 222 ],
223 self.hashtags
224 .iter()
225 .map(|h| {
226 Tag::custom(
227 nostr::TagKind::Custom(std::borrow::Cow::Borrowed("t")),
228 vec![h.clone()],
229 )
230 })
231 .collect(),
220 if self.blossoms.is_empty() { 232 if self.blossoms.is_empty() {
221 vec![] 233 vec![]
222 } else { 234 } else {
@@ -311,6 +323,29 @@ impl RepoRef {
311 pub fn grasp_servers(&self) -> Vec<String> { 323 pub fn grasp_servers(&self) -> Vec<String> {
312 detect_existing_grasp_servers(Some(self), &[], &[], &self.identifier) 324 detect_existing_grasp_servers(Some(self), &[], &[], &self.identifier)
313 } 325 }
326
327 // returns false if already present so didn't need adding
328 pub fn add_grasp_server(&mut self, clone_url: &str) -> Result<bool> {
329 if !is_grasp_server_clone_url(clone_url) {
330 bail!("invalid grasp server clone url. does not end with .git");
331 }
332
333 let relay_url = RelayUrl::parse(
334 &format_grasp_server_url_as_relay_url(clone_url)
335 .context("invalid grasp server clone url")?,
336 )
337 .context("invalid grasp server clone url")?;
338
339 if !self.relays.contains(&relay_url) {
340 self.relays.push(relay_url);
341 }
342 if !self.git_server.contains(&clone_url.to_string()) {
343 self.git_server.push(clone_url.to_string());
344 Ok(true)
345 } else {
346 Ok(false)
347 }
348 }
314} 349}
315 350
316pub async fn get_repo_coordinates_when_remote_unknown( 351pub async fn get_repo_coordinates_when_remote_unknown(
@@ -448,7 +483,7 @@ async fn get_repo_coordinate_from_user_prompt(
448 println!( 483 println!(
449 "{}", 484 "{}",
450 dim.apply_to( 485 dim.apply_to(
451 "hint: https://gitworkshop.dev/repos lists repositories and their nostr address" 486 "hint: https://gitworkshop.dev/search lists repositories and their nostr address"
452 ), 487 ),
453 ); 488 );
454 let git_repo_path = git_repo.get_path()?; 489 let git_repo_path = git_repo.get_path()?;
@@ -699,13 +734,54 @@ pub fn extract_npub(s: &str) -> Result<&str> {
699 } 734 }
700} 735}
701 736
702pub fn is_grasp_server(url: &str, grasp_servers: &[String]) -> bool { 737pub fn is_grasp_server_in_list(url: &str, grasp_servers: &[String]) -> bool {
703 if !grasp_servers.is_empty() { 738 if !grasp_servers.is_empty() {
704 if let Ok(n) = normalize_grasp_server_url(url) { 739 if let Ok(url) = normalize_grasp_server_url(url) {
705 return grasp_servers.contains(&n); 740 grasp_servers.iter().any(|s| {
741 if let Ok(s) = normalize_grasp_server_url(s) {
742 s == url
743 } else {
744 false
745 }
746 })
747 } else {
748 false
706 } 749 }
750 } else {
751 false
752 }
753}
754
755pub fn is_grasp_server_clone_url(url: &str) -> bool {
756 extract_npub(url).is_ok()
757 && (url.ends_with(".git") || url.ends_with(".git/"))
758 && url.starts_with("http")
759}
760
761pub fn format_grasp_server_url_as_relay_url(url: &str) -> Result<String> {
762 let grasp_server_url = normalize_grasp_server_url(url)?;
763 if grasp_server_url.contains("http://") {
764 return Ok(grasp_server_url.replace("http://", "ws://"));
707 } 765 }
708 false 766 Ok(format!("wss://{grasp_server_url}"))
767}
768
769pub fn format_grasp_server_url_as_clone_url(
770 grasp_server: &str,
771 public_key: &PublicKey,
772 identifier: &str,
773) -> Result<String> {
774 let grasp_server_url = normalize_grasp_server_url(grasp_server)?;
775
776 let prefix = if grasp_server_url.contains("http://") {
777 ""
778 } else {
779 "https://"
780 };
781 Ok(format!(
782 "{prefix}{grasp_server_url}/{}/{identifier}.git",
783 public_key.to_bech32()?
784 ))
709} 785}
710 786
711#[cfg(test)] 787#[cfg(test)]
@@ -730,6 +806,7 @@ mod tests {
730 RelayUrl::parse("ws://relay2.io").unwrap(), 806 RelayUrl::parse("ws://relay2.io").unwrap(),
731 ], 807 ],
732 blossoms: vec![], 808 blossoms: vec![],
809 hashtags: vec![],
733 trusted_maintainer: TEST_KEY_1_KEYS.public_key(), 810 trusted_maintainer: TEST_KEY_1_KEYS.public_key(),
734 maintainers_without_annoucnement: None, 811 maintainers_without_annoucnement: None,
735 maintainers: vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()], 812 maintainers: vec![TEST_KEY_1_KEYS.public_key(), TEST_KEY_2_KEYS.public_key()],