upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/git/nostr_url.rs81
-rw-r--r--src/lib/repo_ref.rs16
2 files changed, 84 insertions, 13 deletions
diff --git a/src/lib/git/nostr_url.rs b/src/lib/git/nostr_url.rs
index c26bb2e..ac57538 100644
--- a/src/lib/git/nostr_url.rs
+++ b/src/lib/git/nostr_url.rs
@@ -2,9 +2,11 @@ use core::fmt;
2use std::str::FromStr; 2use std::str::FromStr;
3 3
4use anyhow::{anyhow, bail, Context, Error, Result}; 4use anyhow::{anyhow, bail, Context, Error, Result};
5use nostr::nips::nip01::Coordinate; 5use nostr::nips::{nip01::Coordinate, nip05};
6use nostr_sdk::{PublicKey, RelayUrl, ToBech32, Url}; 6use nostr_sdk::{PublicKey, RelayUrl, ToBech32, Url};
7 7
8use super::{get_git_config_item, save_git_config_item, Repo};
9
8#[derive(Debug, PartialEq, Default, Clone)] 10#[derive(Debug, PartialEq, Default, Clone)]
9pub enum ServerProtocol { 11pub enum ServerProtocol {
10 Ssh, 12 Ssh,
@@ -59,6 +61,7 @@ pub struct NostrUrlDecoded {
59 pub coordinate: Coordinate, 61 pub coordinate: Coordinate,
60 pub protocol: Option<ServerProtocol>, 62 pub protocol: Option<ServerProtocol>,
61 pub user: Option<String>, 63 pub user: Option<String>,
64 pub nip05: Option<String>,
62} 65}
63 66
64impl fmt::Display for NostrUrlDecoded { 67impl fmt::Display for NostrUrlDecoded {
@@ -89,10 +92,8 @@ impl fmt::Display for NostrUrlDecoded {
89 92
90static INCORRECT_NOSTR_URL_FORMAT_ERROR: &str = "incorrect nostr git url format. try nostr://naddr123 or nostr://npub123/my-repo or nostr://ssh/npub123/relay.damus.io/my-repo"; 93static INCORRECT_NOSTR_URL_FORMAT_ERROR: &str = "incorrect nostr git url format. try nostr://naddr123 or nostr://npub123/my-repo or nostr://ssh/npub123/relay.damus.io/my-repo";
91 94
92impl std::str::FromStr for NostrUrlDecoded { 95impl NostrUrlDecoded {
93 type Err = anyhow::Error; 96 pub async fn parse_and_resolve(url: &str, git_repo: &Option<&Repo>) -> Result<Self> {
94
95 fn from_str(url: &str) -> Result<Self> {
96 let mut protocol = None; 97 let mut protocol = None;
97 let mut user = None; 98 let mut user = None;
98 let mut relays = vec![]; 99 let mut relays = vec![];
@@ -154,6 +155,7 @@ impl std::str::FromStr for NostrUrlDecoded {
154 } 155 }
155 // extract naddr npub/<optional-relays>/identifer 156 // extract naddr npub/<optional-relays>/identifer
156 let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?; 157 let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?;
158 let mut nip05 = None;
157 // naddr used 159 // naddr used
158 let coordinate = if let Ok(coordinate) = Coordinate::parse(part) { 160 let coordinate = if let Ok(coordinate) = Coordinate::parse(part) {
159 if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) { 161 if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) {
@@ -161,8 +163,9 @@ impl std::str::FromStr for NostrUrlDecoded {
161 } else { 163 } else {
162 bail!("naddr doesnt point to a git repository announcement"); 164 bail!("naddr doesnt point to a git repository announcement");
163 } 165 }
164 // npub/<optional-relays>/identifer used 166 // <npub|nip05_address>/<optional-relays>/identifer used
165 } else if let Ok(public_key) = PublicKey::parse(part) { 167 } else {
168 let npub_or_nip05 = part.to_owned();
166 parts.remove(0); 169 parts.remove(0);
167 let identifier = parts 170 let identifier = parts
168 .pop() 171 .pop()
@@ -179,14 +182,41 @@ impl std::str::FromStr for NostrUrlDecoded {
179 RelayUrl::parse(&decoded).context("could not parse relays in nostr git url")?; 182 RelayUrl::parse(&decoded).context("could not parse relays in nostr git url")?;
180 relays.push(url); 183 relays.push(url);
181 } 184 }
185 let public_key = match PublicKey::parse(npub_or_nip05) {
186 Ok(public_key) => public_key,
187 Err(_) => {
188 nip05 = Some(npub_or_nip05.to_string());
189 if let Ok(public_key) =
190 resolve_nip05_from_git_config_cache(npub_or_nip05, git_repo)
191 {
192 public_key
193 } else {
194 // TODO eprint loading message
195 let res = nip05::profile(npub_or_nip05, None)
196 .await
197 .context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?;
198 // TODO clear loading message
199 nip05 = Some(npub_or_nip05.to_string());
200 let _ = save_nip05_to_git_config_cache(
201 npub_or_nip05,
202 &res.public_key,
203 git_repo,
204 );
205 if relays.is_empty() {
206 for r in res.relays {
207 relays.push(r);
208 }
209 }
210 res.public_key
211 }
212 }
213 };
182 Coordinate { 214 Coordinate {
183 identifier, 215 identifier,
184 public_key, 216 public_key,
185 kind: nostr_sdk::Kind::GitRepoAnnouncement, 217 kind: nostr_sdk::Kind::GitRepoAnnouncement,
186 relays, 218 relays,
187 } 219 }
188 } else {
189 bail!(INCORRECT_NOSTR_URL_FORMAT_ERROR);
190 }; 220 };
191 221
192 Ok(Self { 222 Ok(Self {
@@ -194,10 +224,43 @@ impl std::str::FromStr for NostrUrlDecoded {
194 coordinate, 224 coordinate,
195 protocol, 225 protocol,
196 user, 226 user,
227 nip05,
197 }) 228 })
198 } 229 }
199} 230}
200 231
232fn resolve_nip05_from_git_config_cache(nip05: &str, git_repo: &Option<&Repo>) -> Result<PublicKey> {
233 let stored_value = get_git_config_item(
234 git_repo,
235 &format!("nostr.nip05.{}", urlencoding::encode(nip05)),
236 )?
237 .context("not in cache")?;
238 PublicKey::parse(stored_value)
239 .context("stored nip05 resolution value did not parse as public key")
240}
241
242fn save_nip05_to_git_config_cache(
243 nip05: &str,
244 public_key: &PublicKey,
245 git_repo: &Option<&Repo>,
246) -> Result<()> {
247 if save_git_config_item(
248 git_repo,
249 &format!("nostr.nip05.{}", urlencoding::encode(nip05)),
250 &public_key.to_bech32()?,
251 )
252 .is_err()
253 {
254 save_git_config_item(
255 &None,
256 &format!("nostr.nip05.{}", urlencoding::encode(nip05)),
257 &public_key.to_bech32()?,
258 )
259 } else {
260 Ok(())
261 }
262}
263
201#[derive(Debug, PartialEq, Default)] 264#[derive(Debug, PartialEq, Default)]
202pub struct CloneUrl { 265pub struct CloneUrl {
203 original_string: String, 266 original_string: String,
diff --git a/src/lib/repo_ref.rs b/src/lib/repo_ref.rs
index 1b25ccf..089befc 100644
--- a/src/lib/repo_ref.rs
+++ b/src/lib/repo_ref.rs
@@ -41,6 +41,7 @@ impl TryFrom<(nostr::Event, Option<PublicKey>)> for RepoRef {
41 type Error = anyhow::Error; 41 type Error = anyhow::Error;
42 42
43 fn try_from((event, trusted_maintainer): (nostr::Event, Option<PublicKey>)) -> Result<Self> { 43 fn try_from((event, trusted_maintainer): (nostr::Event, Option<PublicKey>)) -> Result<Self> {
44 // TODO: turn trusted maintainer into NostrUrlDecoded
44 if !event.kind.eq(&Kind::GitRepoAnnouncement) { 45 if !event.kind.eq(&Kind::GitRepoAnnouncement) {
45 bail!("incorrect kind"); 46 bail!("incorrect kind");
46 } 47 }
@@ -239,6 +240,7 @@ impl RepoRef {
239 coordinate: self.coordinate_with_hint(), 240 coordinate: self.coordinate_with_hint(),
240 protocol: None, 241 protocol: None,
241 user: None, 242 user: None,
243 nip05: None, // TODO: if nip05 for pubkey saved in local git config use it.
242 } 244 }
243 ) 245 )
244 } 246 }
@@ -259,7 +261,7 @@ pub async fn get_repo_coordinates_when_remote_unknown(
259pub async fn try_and_get_repo_coordinates_when_remote_unknown( 261pub async fn try_and_get_repo_coordinates_when_remote_unknown(
260 git_repo: &Repo, 262 git_repo: &Repo,
261) -> Result<Coordinate> { 263) -> Result<Coordinate> {
262 let remote_coordinates = get_repo_coordinates_from_nostr_remotes(git_repo)?; 264 let remote_coordinates = get_repo_coordinates_from_nostr_remotes(git_repo).await?;
263 if remote_coordinates.is_empty() { 265 if remote_coordinates.is_empty() {
264 if let Ok(c) = get_repo_coordinates_from_git_config(git_repo) { 266 if let Ok(c) = get_repo_coordinates_from_git_config(git_repo) {
265 Ok(c) 267 Ok(c)
@@ -327,11 +329,15 @@ fn get_repo_coordinates_from_git_config(git_repo: &Repo) -> Result<Coordinate> {
327 .context("git config item \"nostr.repo\" is not an naddr") 329 .context("git config item \"nostr.repo\" is not an naddr")
328} 330}
329 331
330fn get_repo_coordinates_from_nostr_remotes(git_repo: &Repo) -> Result<HashMap<String, Coordinate>> { 332async fn get_repo_coordinates_from_nostr_remotes(
333 git_repo: &Repo,
334) -> Result<HashMap<String, Coordinate>> {
331 let mut repo_coordinates = HashMap::new(); 335 let mut repo_coordinates = HashMap::new();
332 for remote_name in git_repo.git_repo.remotes()?.iter().flatten() { 336 for remote_name in git_repo.git_repo.remotes()?.iter().flatten() {
333 if let Some(remote_url) = git_repo.git_repo.find_remote(remote_name)?.url() { 337 if let Some(remote_url) = git_repo.git_repo.find_remote(remote_name)?.url() {
334 if let Ok(nostr_url_decoded) = NostrUrlDecoded::from_str(remote_url) { 338 if let Ok(nostr_url_decoded) =
339 NostrUrlDecoded::parse_and_resolve(remote_url, &Some(git_repo)).await
340 {
335 repo_coordinates.insert(remote_name.to_string(), nostr_url_decoded.coordinate); 341 repo_coordinates.insert(remote_name.to_string(), nostr_url_decoded.coordinate);
336 } 342 }
337 } 343 }
@@ -383,7 +389,9 @@ async fn get_repo_coordinate_from_user_prompt(
383 .input(PromptInputParms::default().with_prompt("nostr repository"))?; 389 .input(PromptInputParms::default().with_prompt("nostr repository"))?;
384 let coordinate = if let Ok(c) = Coordinate::parse(&input) { 390 let coordinate = if let Ok(c) = Coordinate::parse(&input) {
385 c 391 c
386 } else if let Ok(nostr_url) = NostrUrlDecoded::from_str(&input) { 392 } else if let Ok(nostr_url) =
393 NostrUrlDecoded::parse_and_resolve(&input, &Some(git_repo)).await
394 {
387 nostr_url.coordinate 395 nostr_url.coordinate
388 } else { 396 } else {
389 eprintln!("not a valid naddr or git nostr remote URL starting nostr://"); 397 eprintln!("not a valid naddr or git nostr remote URL starting nostr://");