From a825311f2c55661aaab3a163bda9109295c96044 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Tue, 3 Sep 2024 15:30:37 +0100 Subject: feat(remote): enhance nostr url format add protocol and user parameters so that users can overide the protcol in the clone url and use specific protocols for fetch and push. see: nostr:nevent1qvzqqqqqqypzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqyxhwumn8ghj7mn0wvhxcmmvqqsp6a5ck6grd9lq0nu25dcfzggxde67erut76w0ucal5rcfq4y5gzc7gmpzm the override feature hasn't been implemented yet but this is an enabler. also added a new format so that macos (zsh) users don't have to use quotes: nostr:///npub123//identifer --- src/git.rs | 145 ++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 109 insertions(+), 36 deletions(-) (limited to 'src/git.rs') diff --git a/src/git.rs b/src/git.rs index 42ff587..5919667 100644 --- a/src/git.rs +++ b/src/git.rs @@ -835,55 +835,128 @@ fn extract_sig_from_patch_tags<'a>( .context("failed to create git signature") } -pub fn nostr_git_url_to_repo_coordinates(url: &str) -> Result> { - let mut repo_coordinattes = HashSet::new(); - let url = Url::parse(url)?; +#[derive(Debug, PartialEq)] +pub enum ServerProtocol { + Ssh, + Https, + Http, + Git, +} - if url.scheme().ne("nostr") { - bail!("nostr git url must start with nostr://") - } +#[derive(Debug, PartialEq)] +pub struct NostrUrlDecoded { + pub coordinates: HashSet, + pub protocol: Option, + pub user: Option, +} - if let Ok(coordinate) = Coordinate::parse(url.domain().context("no naddr")?) { - if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) { - repo_coordinattes.insert(coordinate); - return Ok(repo_coordinattes); +static 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"; + +impl NostrUrlDecoded { + pub fn from_str(url: &str) -> Result { + let mut coordinates = HashSet::new(); + let mut protocol = None; + let mut user = None; + let mut relays = vec![]; + + if !url.starts_with("nostr://") { + bail!("nostr git url must start with nostr://"); + } + // process get url parameters if present + for (name, value) in Url::parse(url)?.query_pairs() { + if name.contains("relay") { + let mut decoded = urlencoding::decode(&value) + .context("could not parse relays in nostr git url")? + .to_string(); + if !decoded.starts_with("ws://") && !decoded.starts_with("wss://") { + decoded = format!("wss://{decoded}"); + } + let url = + Url::parse(&decoded).context("could not parse relays in nostr git url")?; + relays.push(url.to_string()); + } else if name == "protocol" { + protocol = match value.as_ref() { + "ssh" => Some(ServerProtocol::Ssh), + "https" => Some(ServerProtocol::Https), + "http" => Some(ServerProtocol::Http), + "git" => Some(ServerProtocol::Git), + _ => None, + }; + } else if name == "user" { + user = Some(value.to_string()); + } } - bail!("naddr doesnt point to a git repository announcement"); - } - if let Some(domain) = url.domain() { - if let Ok(public_key) = PublicKey::parse(domain) { - if url.path().len() < 2 { - bail!( - "nostr git url should include the repo identifier eg nostr://npub123/the-repo-identifer" - ); + let mut parts: Vec<&str> = url[8..] + .split('?') + .next() + .unwrap_or("") + .split('/') + .collect(); + + // extract optional protocol + if protocol.is_none() { + let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?; + let protocol_str = if let Some(at_index) = part.find('@') { + user = Some(part[..at_index].to_string()); + &part[at_index + 1..] + } else { + part + }; + protocol = match protocol_str { + "ssh" => Some(ServerProtocol::Ssh), + "https" => Some(ServerProtocol::Https), + "http" => Some(ServerProtocol::Http), + "git" => Some(ServerProtocol::Git), + _ => protocol, + }; + if protocol.is_some() { + parts.remove(0); } - let mut relays = vec![]; - for (name, value) in url.query_pairs() { - if name.contains("relay") { - let mut decoded = urlencoding::decode(&value) - .context("could not parse relays in nostr git url")? - .to_string(); - if !decoded.starts_with("ws://") && !decoded.starts_with("wss://") { - decoded = format!("wss://{decoded}"); - } - let url = - Url::parse(&decoded).context("could not parse relays in nostr git url")?; - relays.push(url.to_string()); + } + // extract naddr npub//identifer + let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?; + // naddr used + if let Ok(coordinate) = Coordinate::parse(part) { + if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) { + coordinates.insert(coordinate); + } else { + bail!("naddr doesnt point to a git repository announcement"); + } + // npub//identifer used + } else if let Ok(public_key) = PublicKey::parse(part) { + parts.remove(0); + let identifier = parts + .pop() + .context("nostr url must have an identifier eg. nostr://npub123/repo-identifier")? + .to_string(); + for relay in parts { + let mut decoded = urlencoding::decode(relay) + .context("could not parse relays in nostr git url")? + .to_string(); + if !decoded.starts_with("ws://") && !decoded.starts_with("wss://") { + decoded = format!("wss://{decoded}"); } + let url = + Url::parse(&decoded).context("could not parse relays in nostr git url")?; + relays.push(url.to_string()); } - repo_coordinattes.insert(Coordinate { - identifier: url.path()[1..].to_string(), + coordinates.insert(Coordinate { + identifier, public_key, kind: nostr_sdk::Kind::GitRepoAnnouncement, relays, }); - return Ok(repo_coordinattes); + } else { + bail!(INCORRECT_NOSTR_URL_FORMAT_ERROR); } + + Ok(Self { + coordinates, + protocol, + user, + }) } - bail!( - "nostr git url must be in format nostr://naddr123 or nostr://npub123/identifer?relay=wss://relay-example.com&relay1=wss://relay-example.org" - ); } /** produce error when using local repo or custom protocols */ -- cgit v1.2.3