upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/git_remote_nostr/list.rs138
-rw-r--r--src/bin/git_remote_nostr/main.rs18
-rw-r--r--src/bin/git_remote_nostr/push.rs16
-rw-r--r--src/lib/git/nostr_url.rs171
4 files changed, 229 insertions, 114 deletions
diff --git a/src/bin/git_remote_nostr/list.rs b/src/bin/git_remote_nostr/list.rs
index 097f070..26f699d 100644
--- a/src/bin/git_remote_nostr/list.rs
+++ b/src/bin/git_remote_nostr/list.rs
@@ -1,25 +1,35 @@
1use core::str; 1use core::str;
2use std::collections::HashMap; 2use std::collections::HashMap;
3 3
4use anyhow::{Context, Result}; 4use anyhow::{anyhow, Context, Result};
5use auth_git2::GitAuthenticator; 5use auth_git2::GitAuthenticator;
6use client::get_state_from_cache; 6use client::get_state_from_cache;
7use git::RepoActions; 7use git::RepoActions;
8use git_events::{event_to_cover_letter, get_commit_id_from_patch}; 8use git_events::{event_to_cover_letter, get_commit_id_from_patch};
9use ngit::{client, git, git_events, login::get_curent_user, repo_ref}; 9use ngit::{
10 client,
11 git::{
12 self,
13 nostr_url::{CloneUrl, NostrUrlDecoded, ServerProtocol},
14 },
15 git_events,
16 login::get_curent_user,
17 repo_ref,
18};
10use nostr_sdk::hashes::sha1::Hash as Sha1Hash; 19use nostr_sdk::hashes::sha1::Hash as Sha1Hash;
11use repo_ref::RepoRef; 20use repo_ref::RepoRef;
12 21
13use crate::{ 22use crate::{
14 git::Repo, 23 git::Repo,
15 utils::{ 24 utils::{
16 get_open_proposals, get_short_git_server_name, switch_clone_url_between_ssh_and_https, 25 get_open_proposals, get_read_protocols_to_try, get_short_git_server_name, join_with_and,
17 }, 26 },
18}; 27};
19 28
20pub async fn run_list( 29pub async fn run_list(
21 git_repo: &Repo, 30 git_repo: &Repo,
22 repo_ref: &RepoRef, 31 repo_ref: &RepoRef,
32 decoded_nostr_url: &NostrUrlDecoded,
23 for_push: bool, 33 for_push: bool,
24) -> Result<HashMap<String, HashMap<String, String>>> { 34) -> Result<HashMap<String, HashMap<String, String>>> {
25 let nostr_state = 35 let nostr_state =
@@ -31,7 +41,7 @@ pub async fn run_list(
31 41
32 let term = console::Term::stderr(); 42 let term = console::Term::stderr();
33 43
34 let remote_states = list_from_remotes(&term, git_repo, &repo_ref.git_server)?; 44 let remote_states = list_from_remotes(&term, git_repo, &repo_ref.git_server, decoded_nostr_url);
35 45
36 let mut state = if let Some(nostr_state) = nostr_state { 46 let mut state = if let Some(nostr_state) = nostr_state {
37 for (name, value) in &nostr_state.state { 47 for (name, value) in &nostr_state.state {
@@ -122,41 +132,115 @@ pub fn list_from_remotes(
122 term: &console::Term, 132 term: &console::Term,
123 git_repo: &Repo, 133 git_repo: &Repo,
124 git_servers: &Vec<String>, 134 git_servers: &Vec<String>,
125) -> Result<HashMap<String, HashMap<String, String>>> { 135 decoded_nostr_url: &NostrUrlDecoded, // Add this parameter
136) -> HashMap<String, HashMap<String, String>> {
126 let mut remote_states = HashMap::new(); 137 let mut remote_states = HashMap::new();
138 let mut errors = HashMap::new();
127 for url in git_servers { 139 for url in git_servers {
128 let short_name = get_short_git_server_name(git_repo, url); 140 match list_from_remote(term, git_repo, url, decoded_nostr_url) {
129 term.write_line(format!("fetching refs list: {short_name}...").as_str())?; 141 Err(error) => {
130 match list_from_remote(git_repo, url) { 142 errors.insert(url, error);
131 Ok(remote_state) => {
132 remote_states.insert(url.clone(), remote_state);
133 } 143 }
134 Err(error1) => { 144 Ok(state) => {
135 if let Ok(alternative_url) = switch_clone_url_between_ssh_and_https(url) { 145 remote_states.insert(url.to_string(), state);
136 match list_from_remote(git_repo, &alternative_url) { 146 }
137 Ok(remote_state) => { 147 }
138 remote_states.insert(url.clone(), remote_state); 148 }
139 } 149 remote_states
140 Err(error2) => { 150}
141 term.write_line( 151
142 format!("WARNING: {short_name} failed to list refs error: {error1}\r\nand alternative protocol {alternative_url}: {error2}").as_str(), 152pub fn list_from_remote(
143 )?; 153 term: &console::Term,
144 } 154 git_repo: &Repo,
145 } 155 git_server_url: &str,
146 } else { 156 decoded_nostr_url: &NostrUrlDecoded, // Add this parameter
157) -> Result<HashMap<String, String>> {
158 let server_url = git_server_url.parse::<CloneUrl>()?;
159 let protocols_to_attempt = get_read_protocols_to_try(&server_url, decoded_nostr_url);
160
161 let mut failed_protocols = vec![];
162 let mut remote_state: Option<HashMap<String, String>> = None;
163
164 for protocol in &protocols_to_attempt {
165 term.write_line(
166 format!(
167 "fetching ref list from {} over {protocol}...",
168 server_url.domain(),
169 )
170 .as_str(),
171 )?;
172
173 let formatted_url = server_url.format_as(protocol, &decoded_nostr_url.user)?;
174 let res = if [ServerProtocol::UnauthHttps, ServerProtocol::UnauthHttp].contains(protocol) {
175 list_from_remote_url_unauthenticated(git_repo, &formatted_url)
176 } else {
177 list_from_remote_url(git_repo, &formatted_url)
178 };
179
180 match res {
181 Ok(state) => {
182 remote_state = Some(state);
183 if !failed_protocols.is_empty() {
147 term.write_line( 184 term.write_line(
148 format!("WARNING: {short_name} failed to list refs error: {error1}",) 185 format!(
149 .as_str(), 186 "list: succeeded over {protocol} for {}",
187 server_url.domain(),
188 )
189 .as_str(),
150 )?; 190 )?;
151 } 191 }
192 break;
193 }
194 Err(error) => {
195 term.write_line(
196 format!("list: {formatted_url} failed over {protocol}: {error}").as_str(),
197 )?;
198 failed_protocols.push(protocol);
152 } 199 }
153 } 200 }
154 term.clear_last_lines(1)?; 201 term.clear_last_lines(1)?;
155 } 202 }
156 Ok(remote_states) 203 if let Some(remote_state) = remote_state {
204 Ok(remote_state)
205 } else {
206 let error = anyhow!(
207 "{} failed over {}{}",
208 server_url.domain(),
209 join_with_and(&failed_protocols),
210 if decoded_nostr_url.protocol.is_some() {
211 " and nostr url contains protocol override so no other protocols were attempted"
212 } else {
213 ""
214 },
215 );
216 term.write_line(format!("list: {error}").as_str())?;
217 Err(error)
218 }
219}
220
221fn list_from_remote_url_unauthenticated(
222 git_repo: &Repo,
223 git_server_remote_url: &str,
224) -> Result<HashMap<String, String>> {
225 let mut git_server_remote = git_repo.git_repo.remote_anonymous(git_server_remote_url)?;
226 let remote_callbacks = git2::RemoteCallbacks::new();
227 git_server_remote.connect_auth(git2::Direction::Fetch, Some(remote_callbacks), None)?;
228 let mut state = HashMap::new();
229 for head in git_server_remote.list()? {
230 if let Some(symbolic_reference) = head.symref_target() {
231 state.insert(
232 head.name().to_string(),
233 format!("ref: {symbolic_reference}"),
234 );
235 } else {
236 state.insert(head.name().to_string(), head.oid().to_string());
237 }
238 }
239 git_server_remote.disconnect()?;
240 Ok(state)
157} 241}
158 242
159fn list_from_remote( 243fn list_from_remote_url(
160 git_repo: &Repo, 244 git_repo: &Repo,
161 git_server_remote_url: &str, 245 git_server_remote_url: &str,
162) -> Result<HashMap<String, String>> { 246) -> Result<HashMap<String, String>> {
diff --git a/src/bin/git_remote_nostr/main.rs b/src/bin/git_remote_nostr/main.rs
index a3fc5f8..e2738d2 100644
--- a/src/bin/git_remote_nostr/main.rs
+++ b/src/bin/git_remote_nostr/main.rs
@@ -75,13 +75,21 @@ async fn main() -> Result<()> {
75 println!("unsupported"); 75 println!("unsupported");
76 } 76 }
77 ["fetch", oid, refstr] => { 77 ["fetch", oid, refstr] => {
78 fetch::run_fetch(&git_repo, &repo_ref, &decoded_nostr_url, &stdin, oid, refstr).await?; 78 fetch::run_fetch(
79 &git_repo,
80 &repo_ref,
81 &decoded_nostr_url,
82 &stdin,
83 oid,
84 refstr,
85 )
86 .await?;
79 } 87 }
80 ["push", refspec] => { 88 ["push", refspec] => {
81 push::run_push( 89 push::run_push(
82 &git_repo, 90 &git_repo,
83 &repo_ref, 91 &repo_ref,
84 nostr_remote_url, 92 &decoded_nostr_url,
85 &stdin, 93 &stdin,
86 refspec, 94 refspec,
87 &client, 95 &client,
@@ -90,10 +98,12 @@ async fn main() -> Result<()> {
90 .await?; 98 .await?;
91 } 99 }
92 ["list"] => { 100 ["list"] => {
93 list_outputs = Some(list::run_list(&git_repo, &repo_ref, false).await?); 101 list_outputs =
102 Some(list::run_list(&git_repo, &repo_ref, &decoded_nostr_url, false).await?);
94 } 103 }
95 ["list", "for-push"] => { 104 ["list", "for-push"] => {
96 list_outputs = Some(list::run_list(&git_repo, &repo_ref, true).await?); 105 list_outputs =
106 Some(list::run_list(&git_repo, &repo_ref, &decoded_nostr_url, true).await?);
97 } 107 }
98 [] => { 108 [] => {
99 return Ok(()); 109 return Ok(());
diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs
index 441c341..2c9c7e1 100644
--- a/src/bin/git_remote_nostr/push.rs
+++ b/src/bin/git_remote_nostr/push.rs
@@ -15,7 +15,7 @@ use git_events::{
15}; 15};
16use ngit::{ 16use ngit::{
17 client::{self, get_event_from_cache_by_id}, 17 client::{self, get_event_from_cache_by_id},
18 git, 18 git::{self, nostr_url::NostrUrlDecoded},
19 git_events::{self, get_event_root}, 19 git_events::{self, get_event_root},
20 login::{self, get_curent_user}, 20 login::{self, get_curent_user},
21 repo_ref, repo_state, 21 repo_ref, repo_state,
@@ -42,7 +42,7 @@ use crate::{
42pub async fn run_push( 42pub async fn run_push(
43 git_repo: &Repo, 43 git_repo: &Repo,
44 repo_ref: &RepoRef, 44 repo_ref: &RepoRef,
45 nostr_remote_url: &str, 45 decoded_nostr_url: &NostrUrlDecoded,
46 stdin: &Stdin, 46 stdin: &Stdin,
47 initial_refspec: &str, 47 initial_refspec: &str,
48 client: &Client, 48 client: &Client,
@@ -66,7 +66,7 @@ pub async fn run_push(
66 66
67 let list_outputs = match list_outputs { 67 let list_outputs = match list_outputs {
68 Some(outputs) => outputs, 68 Some(outputs) => outputs,
69 _ => list_from_remotes(&term, git_repo, &repo_ref.git_server)?, 69 _ => list_from_remotes(&term, git_repo, &repo_ref.git_server, decoded_nostr_url),
70 }; 70 };
71 71
72 let nostr_state = get_state_from_cache(git_repo.get_path()?, repo_ref).await; 72 let nostr_state = get_state_from_cache(git_repo.get_path()?, repo_ref).await;
@@ -154,7 +154,7 @@ pub async fn run_push(
154 &term, 154 &term,
155 repo_ref, 155 repo_ref,
156 git_repo, 156 git_repo,
157 nostr_remote_url, 157 &decoded_nostr_url.original_string,
158 &signer, 158 &signer,
159 &git_server_refspecs, 159 &git_server_refspecs,
160 ) 160 )
@@ -306,8 +306,12 @@ pub async fn run_push(
306 } 306 }
307 let (_, to) = refspec_to_from_to(refspec)?; 307 let (_, to) = refspec_to_from_to(refspec)?;
308 println!("ok {to}"); 308 println!("ok {to}");
309 update_remote_refs_pushed(&git_repo.git_repo, refspec, nostr_remote_url) 309 update_remote_refs_pushed(
310 .context("could not update remote_ref locally")?; 310 &git_repo.git_repo,
311 refspec,
312 &decoded_nostr_url.original_string,
313 )
314 .context("could not update remote_ref locally")?;
311 } 315 }
312 316
313 // TODO make async - check gitlib2 callbacks work async 317 // TODO make async - check gitlib2 callbacks work async
diff --git a/src/lib/git/nostr_url.rs b/src/lib/git/nostr_url.rs
index ea8d221..2717916 100644
--- a/src/lib/git/nostr_url.rs
+++ b/src/lib/git/nostr_url.rs
@@ -36,6 +36,7 @@ impl fmt::Display for ServerProtocol {
36 36
37#[derive(Debug, PartialEq)] 37#[derive(Debug, PartialEq)]
38pub struct NostrUrlDecoded { 38pub struct NostrUrlDecoded {
39 pub original_string: String,
39 pub coordinates: HashSet<Coordinate>, 40 pub coordinates: HashSet<Coordinate>,
40 pub protocol: Option<ServerProtocol>, 41 pub protocol: Option<ServerProtocol>,
41 pub user: Option<String>, 42 pub user: Option<String>,
@@ -145,6 +146,7 @@ impl std::str::FromStr for NostrUrlDecoded {
145 } 146 }
146 147
147 Ok(Self { 148 Ok(Self {
149 original_string: url.to_string(),
148 coordinates, 150 coordinates,
149 protocol, 151 protocol,
150 user, 152 user,
@@ -831,11 +833,11 @@ mod tests {
831 833
832 #[test] 834 #[test]
833 fn from_naddr() -> Result<()> { 835 fn from_naddr() -> Result<()> {
836 let url = "nostr://naddr1qqzxuemfwsqs6amnwvaz7tmwdaejumr0dspzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqvzqqqrhnym0k2qj".to_string();
834 assert_eq!( 837 assert_eq!(
835 NostrUrlDecoded::from_str( 838 NostrUrlDecoded::from_str(&url)?,
836 "nostr://naddr1qqzxuemfwsqs6amnwvaz7tmwdaejumr0dspzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqvzqqqrhnym0k2qj"
837 )?,
838 NostrUrlDecoded { 839 NostrUrlDecoded {
840 original_string: url.clone(),
839 coordinates: HashSet::from([Coordinate { 841 coordinates: HashSet::from([Coordinate {
840 identifier: "ngit".to_string(), 842 identifier: "ngit".to_string(),
841 public_key: PublicKey::parse( 843 public_key: PublicKey::parse(
@@ -851,16 +853,19 @@ mod tests {
851 ); 853 );
852 Ok(()) 854 Ok(())
853 } 855 }
856
854 mod from_npub_slash_identifier { 857 mod from_npub_slash_identifier {
855 use super::*; 858 use super::*;
856 859
857 #[test] 860 #[test]
858 fn without_relay() -> Result<()> { 861 fn without_relay() -> Result<()> {
862 let url =
863 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit"
864 .to_string();
859 assert_eq!( 865 assert_eq!(
860 NostrUrlDecoded::from_str( 866 NostrUrlDecoded::from_str(&url)?,
861 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit"
862 )?,
863 NostrUrlDecoded { 867 NostrUrlDecoded {
868 original_string: url.clone(),
864 coordinates: HashSet::from([get_model_coordinate(false)]), 869 coordinates: HashSet::from([get_model_coordinate(false)]),
865 protocol: None, 870 protocol: None,
866 user: None, 871 user: None,
@@ -870,16 +875,15 @@ mod tests {
870 } 875 }
871 876
872 mod with_url_parameters { 877 mod with_url_parameters {
873
874 use super::*; 878 use super::*;
875 879
876 #[test] 880 #[test]
877 fn with_relay_without_scheme_defaults_to_wss() -> Result<()> { 881 fn with_relay_without_scheme_defaults_to_wss() -> Result<()> {
882 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay=nos.lol".to_string();
878 assert_eq!( 883 assert_eq!(
879 NostrUrlDecoded::from_str( 884 NostrUrlDecoded::from_str(&url)?,
880 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay=nos.lol"
881 )?,
882 NostrUrlDecoded { 885 NostrUrlDecoded {
886 original_string: url.clone(),
883 coordinates: HashSet::from([get_model_coordinate(true)]), 887 coordinates: HashSet::from([get_model_coordinate(true)]),
884 protocol: None, 888 protocol: None,
885 user: None, 889 user: None,
@@ -890,12 +894,14 @@ mod tests {
890 894
891 #[test] 895 #[test]
892 fn with_encoded_relay() -> Result<()> { 896 fn with_encoded_relay() -> Result<()> {
897 let url = format!(
898 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}",
899 urlencoding::encode("wss://nos.lol")
900 );
893 assert_eq!( 901 assert_eq!(
894 NostrUrlDecoded::from_str(&format!( 902 NostrUrlDecoded::from_str(&url)?,
895 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}",
896 urlencoding::encode("wss://nos.lol")
897 ))?,
898 NostrUrlDecoded { 903 NostrUrlDecoded {
904 original_string: url.clone(),
899 coordinates: HashSet::from([get_model_coordinate(true)]), 905 coordinates: HashSet::from([get_model_coordinate(true)]),
900 protocol: None, 906 protocol: None,
901 user: None, 907 user: None,
@@ -903,41 +909,44 @@ mod tests {
903 ); 909 );
904 Ok(()) 910 Ok(())
905 } 911 }
912
906 #[test] 913 #[test]
907 fn with_multiple_encoded_relays() -> Result<()> { 914 fn with_multiple_encoded_relays() -> Result<()> {
908 assert_eq!( 915 let url = format!(
909 NostrUrlDecoded::from_str(&format!( 916 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}&relay1={}",
910 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}&relay1={}", 917 urlencoding::encode("wss://nos.lol"),
911 urlencoding::encode("wss://nos.lol"), 918 urlencoding::encode("wss://relay.damus.io"),
912 urlencoding::encode("wss://relay.damus.io"),
913 ))?,
914 NostrUrlDecoded {
915 coordinates: HashSet::from([Coordinate {
916 identifier: "ngit".to_string(),
917 public_key: PublicKey::parse(
918 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
919 )
920 .unwrap(),
921 kind: nostr_sdk::Kind::GitRepoAnnouncement,
922 relays: vec![
923 "wss://nos.lol/".to_string(),
924 "wss://relay.damus.io/".to_string(),
925 ],
926 }]),
927 protocol: None,
928 user: None,
929 },
930 ); 919 );
920 assert_eq!(
921 NostrUrlDecoded::from_str(&url)?,
922 NostrUrlDecoded {
923 original_string: url.clone(),
924 coordinates: HashSet::from([Coordinate {
925 identifier: "ngit".to_string(),
926 public_key: PublicKey::parse(
927 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
928 )
929 .unwrap(),
930 kind: nostr_sdk::Kind::GitRepoAnnouncement,
931 relays: vec![
932 "wss://nos.lol/".to_string(),
933 "wss://relay.damus.io/".to_string(),
934 ],
935 }]),
936 protocol: None,
937 user: None,
938 },
939 );
931 Ok(()) 940 Ok(())
932 } 941 }
933 942
934 #[test] 943 #[test]
935 fn with_server_protocol() -> Result<()> { 944 fn with_server_protocol() -> Result<()> {
945 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh".to_string();
936 assert_eq!( 946 assert_eq!(
937 NostrUrlDecoded::from_str( 947 NostrUrlDecoded::from_str(&url)?,
938 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh"
939 )?,
940 NostrUrlDecoded { 948 NostrUrlDecoded {
949 original_string: url.clone(),
941 coordinates: HashSet::from([get_model_coordinate(false)]), 950 coordinates: HashSet::from([get_model_coordinate(false)]),
942 protocol: Some(ServerProtocol::Ssh), 951 protocol: Some(ServerProtocol::Ssh),
943 user: None, 952 user: None,
@@ -945,13 +954,14 @@ mod tests {
945 ); 954 );
946 Ok(()) 955 Ok(())
947 } 956 }
957
948 #[test] 958 #[test]
949 fn with_server_protocol_and_user() -> Result<()> { 959 fn with_server_protocol_and_user() -> Result<()> {
960 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh&user=fred".to_string();
950 assert_eq!( 961 assert_eq!(
951 NostrUrlDecoded::from_str( 962 NostrUrlDecoded::from_str(&url)?,
952 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh&user=fred"
953 )?,
954 NostrUrlDecoded { 963 NostrUrlDecoded {
964 original_string: url.clone(),
955 coordinates: HashSet::from([get_model_coordinate(false)]), 965 coordinates: HashSet::from([get_model_coordinate(false)]),
956 protocol: Some(ServerProtocol::Ssh), 966 protocol: Some(ServerProtocol::Ssh),
957 user: Some("fred".to_string()), 967 user: Some("fred".to_string()),
@@ -960,16 +970,17 @@ mod tests {
960 Ok(()) 970 Ok(())
961 } 971 }
962 } 972 }
973
963 mod with_parameters_embedded_with_slashes { 974 mod with_parameters_embedded_with_slashes {
964 use super::*; 975 use super::*;
965 976
966 #[test] 977 #[test]
967 fn with_relay_without_scheme_defaults_to_wss() -> Result<()> { 978 fn with_relay_without_scheme_defaults_to_wss() -> Result<()> {
979 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit".to_string();
968 assert_eq!( 980 assert_eq!(
969 NostrUrlDecoded::from_str( 981 NostrUrlDecoded::from_str(&url)?,
970 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit"
971 )?,
972 NostrUrlDecoded { 982 NostrUrlDecoded {
983 original_string: url.clone(),
973 coordinates: HashSet::from([get_model_coordinate(true)]), 984 coordinates: HashSet::from([get_model_coordinate(true)]),
974 protocol: None, 985 protocol: None,
975 user: None, 986 user: None,
@@ -980,12 +991,14 @@ mod tests {
980 991
981 #[test] 992 #[test]
982 fn with_encoded_relay() -> Result<()> { 993 fn with_encoded_relay() -> Result<()> {
994 let url = format!(
995 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/ngit",
996 urlencoding::encode("wss://nos.lol")
997 );
983 assert_eq!( 998 assert_eq!(
984 NostrUrlDecoded::from_str(&format!( 999 NostrUrlDecoded::from_str(&url)?,
985 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/ngit",
986 urlencoding::encode("wss://nos.lol")
987 ))?,
988 NostrUrlDecoded { 1000 NostrUrlDecoded {
1001 original_string: url.clone(),
989 coordinates: HashSet::from([get_model_coordinate(true)]), 1002 coordinates: HashSet::from([get_model_coordinate(true)]),
990 protocol: None, 1003 protocol: None,
991 user: None, 1004 user: None,
@@ -993,41 +1006,44 @@ mod tests {
993 ); 1006 );
994 Ok(()) 1007 Ok(())
995 } 1008 }
1009
996 #[test] 1010 #[test]
997 fn with_multiple_encoded_relays() -> Result<()> { 1011 fn with_multiple_encoded_relays() -> Result<()> {
998 assert_eq!( 1012 let url = format!(
999 NostrUrlDecoded::from_str(&format!( 1013 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/{}/ngit",
1000 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/{}/ngit", 1014 urlencoding::encode("wss://nos.lol"),
1001 urlencoding::encode("wss://nos.lol"), 1015 urlencoding::encode("wss://relay.damus.io"),
1002 urlencoding::encode("wss://relay.damus.io"),
1003 ))?,
1004 NostrUrlDecoded {
1005 coordinates: HashSet::from([Coordinate {
1006 identifier: "ngit".to_string(),
1007 public_key: PublicKey::parse(
1008 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
1009 )
1010 .unwrap(),
1011 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1012 relays: vec![
1013 "wss://nos.lol/".to_string(),
1014 "wss://relay.damus.io/".to_string(),
1015 ],
1016 }]),
1017 protocol: None,
1018 user: None,
1019 },
1020 ); 1016 );
1017 assert_eq!(
1018 NostrUrlDecoded::from_str(&url)?,
1019 NostrUrlDecoded {
1020 original_string: url.clone(),
1021 coordinates: HashSet::from([Coordinate {
1022 identifier: "ngit".to_string(),
1023 public_key: PublicKey::parse(
1024 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
1025 )
1026 .unwrap(),
1027 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1028 relays: vec![
1029 "wss://nos.lol/".to_string(),
1030 "wss://relay.damus.io/".to_string(),
1031 ],
1032 }]),
1033 protocol: None,
1034 user: None,
1035 },
1036 );
1021 Ok(()) 1037 Ok(())
1022 } 1038 }
1023 1039
1024 #[test] 1040 #[test]
1025 fn with_server_protocol() -> Result<()> { 1041 fn with_server_protocol() -> Result<()> {
1042 let url = "nostr://ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit".to_string();
1026 assert_eq!( 1043 assert_eq!(
1027 NostrUrlDecoded::from_str( 1044 NostrUrlDecoded::from_str(&url)?,
1028 "nostr://ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit"
1029 )?,
1030 NostrUrlDecoded { 1045 NostrUrlDecoded {
1046 original_string: url.clone(),
1031 coordinates: HashSet::from([get_model_coordinate(false)]), 1047 coordinates: HashSet::from([get_model_coordinate(false)]),
1032 protocol: Some(ServerProtocol::Ssh), 1048 protocol: Some(ServerProtocol::Ssh),
1033 user: None, 1049 user: None,
@@ -1035,13 +1051,14 @@ mod tests {
1035 ); 1051 );
1036 Ok(()) 1052 Ok(())
1037 } 1053 }
1054
1038 #[test] 1055 #[test]
1039 fn with_server_protocol_and_user() -> Result<()> { 1056 fn with_server_protocol_and_user() -> Result<()> {
1057 let url = "nostr://fred@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit".to_string();
1040 assert_eq!( 1058 assert_eq!(
1041 NostrUrlDecoded::from_str( 1059 NostrUrlDecoded::from_str(&url)?,
1042 "nostr://fred@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit"
1043 )?,
1044 NostrUrlDecoded { 1060 NostrUrlDecoded {
1061 original_string: url.clone(),
1045 coordinates: HashSet::from([get_model_coordinate(false)]), 1062 coordinates: HashSet::from([get_model_coordinate(false)]),
1046 protocol: Some(ServerProtocol::Ssh), 1063 protocol: Some(ServerProtocol::Ssh),
1047 user: Some("fred".to_string()), 1064 user: Some("fred".to_string()),