upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/lib/git/nostr_url.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/git/nostr_url.rs')
-rw-r--r--src/lib/git/nostr_url.rs203
1 files changed, 153 insertions, 50 deletions
diff --git a/src/lib/git/nostr_url.rs b/src/lib/git/nostr_url.rs
index c26bb2e..ca616a0 100644
--- a/src/lib/git/nostr_url.rs
+++ b/src/lib/git/nostr_url.rs
@@ -1,10 +1,12 @@
1use core::fmt; 1use core::fmt;
2use std::str::FromStr; 2use std::{collections::HashMap, 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![];
@@ -135,7 +136,9 @@ impl std::str::FromStr for NostrUrlDecoded {
135 // extract optional protocol 136 // extract optional protocol
136 if protocol.is_none() { 137 if protocol.is_none() {
137 let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?; 138 let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?;
138 let protocol_str = if let Some(at_index) = part.find('@') { 139 let protocol_str = if part.contains('.') {
140 part
141 } else if let Some(at_index) = part.find('@') {
139 user = Some(part[..at_index].to_string()); 142 user = Some(part[..at_index].to_string());
140 &part[at_index + 1..] 143 &part[at_index + 1..]
141 } else { 144 } else {
@@ -146,7 +149,7 @@ impl std::str::FromStr for NostrUrlDecoded {
146 "https" => Some(ServerProtocol::Https), 149 "https" => Some(ServerProtocol::Https),
147 "http" => Some(ServerProtocol::Http), 150 "http" => Some(ServerProtocol::Http),
148 "git" => Some(ServerProtocol::Git), 151 "git" => Some(ServerProtocol::Git),
149 _ => protocol, 152 _ => None,
150 }; 153 };
151 if protocol.is_some() { 154 if protocol.is_some() {
152 parts.remove(0); 155 parts.remove(0);
@@ -154,6 +157,7 @@ impl std::str::FromStr for NostrUrlDecoded {
154 } 157 }
155 // extract naddr npub/<optional-relays>/identifer 158 // extract naddr npub/<optional-relays>/identifer
156 let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?; 159 let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?;
160 let mut nip05 = None;
157 // naddr used 161 // naddr used
158 let coordinate = if let Ok(coordinate) = Coordinate::parse(part) { 162 let coordinate = if let Ok(coordinate) = Coordinate::parse(part) {
159 if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) { 163 if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) {
@@ -161,8 +165,9 @@ impl std::str::FromStr for NostrUrlDecoded {
161 } else { 165 } else {
162 bail!("naddr doesnt point to a git repository announcement"); 166 bail!("naddr doesnt point to a git repository announcement");
163 } 167 }
164 // npub/<optional-relays>/identifer used 168 // <npub|nip05_address>/<optional-relays>/identifer used
165 } else if let Ok(public_key) = PublicKey::parse(part) { 169 } else {
170 let npub_or_nip05 = part.to_owned();
166 parts.remove(0); 171 parts.remove(0);
167 let identifier = parts 172 let identifier = parts
168 .pop() 173 .pop()
@@ -179,14 +184,46 @@ impl std::str::FromStr for NostrUrlDecoded {
179 RelayUrl::parse(&decoded).context("could not parse relays in nostr git url")?; 184 RelayUrl::parse(&decoded).context("could not parse relays in nostr git url")?;
180 relays.push(url); 185 relays.push(url);
181 } 186 }
187 let public_key = match PublicKey::parse(npub_or_nip05) {
188 Ok(public_key) => public_key,
189 Err(_) => {
190 nip05 = Some(npub_or_nip05.to_string());
191 if let Ok(public_key) =
192 resolve_nip05_from_git_config_cache(npub_or_nip05, git_repo)
193 {
194 public_key
195 } else {
196 let term = console::Term::stderr();
197 let domain = {
198 let s = npub_or_nip05.split('@').collect::<Vec<&str>>();
199 if s.len() == 2 { s[1] } else { s[0] }
200 };
201 term.write_line(&format!("fetching pubic key info from {domain}..."))?;
202 let res = nip05::profile(npub_or_nip05, None)
203 .await
204 .context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?;
205 term.clear_last_lines(1)?;
206 nip05 = Some(npub_or_nip05.to_string());
207 let _ = save_nip05_to_git_config_cache(
208 npub_or_nip05,
209 &res.public_key,
210 git_repo,
211 );
212 if relays.is_empty() {
213 for r in res.relays {
214 relays.push(r);
215 }
216 }
217 res.public_key
218 }
219 }
220 };
182 Coordinate { 221 Coordinate {
183 identifier, 222 identifier,
184 public_key, 223 public_key,
185 kind: nostr_sdk::Kind::GitRepoAnnouncement, 224 kind: nostr_sdk::Kind::GitRepoAnnouncement,
186 relays, 225 relays,
187 } 226 }
188 } else {
189 bail!(INCORRECT_NOSTR_URL_FORMAT_ERROR);
190 }; 227 };
191 228
192 Ok(Self { 229 Ok(Self {
@@ -194,10 +231,62 @@ impl std::str::FromStr for NostrUrlDecoded {
194 coordinate, 231 coordinate,
195 protocol, 232 protocol,
196 user, 233 user,
234 nip05,
197 }) 235 })
198 } 236 }
199} 237}
200 238
239fn resolve_nip05_from_git_config_cache(nip05: &str, git_repo: &Option<&Repo>) -> Result<PublicKey> {
240 if let Some(public_key) = load_nip_cache(git_repo)?.get(nip05) {
241 Ok(*public_key)
242 } else {
243 bail!("nip05 not stored in local git config cache")
244 }
245}
246
247pub fn use_nip05_git_config_cache_to_find_nip05_from_public_key(
248 public_key: &PublicKey,
249 git_repo: &Option<&Repo>,
250) -> Result<Option<String>> {
251 let h = load_nip_cache(git_repo)?;
252 Ok(h.iter()
253 .find_map(|(k, v)| if *v == *public_key { Some(k) } else { None })
254 .cloned())
255}
256
257fn save_nip05_to_git_config_cache(
258 nip05: &str,
259 public_key: &PublicKey,
260 git_repo: &Option<&Repo>,
261) -> Result<()> {
262 let mut h = load_nip_cache(git_repo)?;
263 h.insert(nip05.to_string(), *public_key);
264
265 let s = h
266 .into_iter()
267 .map(|(nip05, public_key)| format!("{nip05}:{}", public_key.to_hex()))
268 .collect::<Vec<String>>()
269 .join(",");
270
271 save_git_config_item(git_repo, "nostr.nip05", s.as_str())
272 .context("could not save nip05 cache in git config")
273}
274
275fn load_nip_cache(git_repo: &Option<&Repo>) -> Result<HashMap<String, PublicKey>> {
276 let mut h = HashMap::new();
277 let stored_value = get_git_config_item(git_repo, "nostr.nip05")?
278 .context("no nip05s in local git config cache so retun empty cache")
279 .unwrap_or_default();
280 for pair in stored_value.split(',') {
281 if let Some((cached_nip05, pubkey)) = pair.split_once(':') {
282 if let Ok(public_key) = PublicKey::parse(pubkey) {
283 h.insert(cached_nip05.to_string(), public_key);
284 }
285 }
286 }
287 Ok(h)
288}
289
201#[derive(Debug, PartialEq, Default)] 290#[derive(Debug, PartialEq, Default)]
202pub struct CloneUrl { 291pub struct CloneUrl {
203 original_string: String, 292 original_string: String,
@@ -887,6 +976,7 @@ mod tests {
887 }, 976 },
888 protocol: None, 977 protocol: None,
889 user: None, 978 user: None,
979 nip05: None,
890 } 980 }
891 ), 981 ),
892 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit", 982 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit",
@@ -912,6 +1002,7 @@ mod tests {
912 }, 1002 },
913 protocol: None, 1003 protocol: None,
914 user: None, 1004 user: None,
1005 nip05: None,
915 } 1006 }
916 ), 1007 ),
917 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit", 1008 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit",
@@ -937,6 +1028,7 @@ mod tests {
937 }, 1028 },
938 protocol: Some(ServerProtocol::Ssh), 1029 protocol: Some(ServerProtocol::Ssh),
939 user: None, 1030 user: None,
1031 nip05: None,
940 } 1032 }
941 ), 1033 ),
942 "nostr://ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit", 1034 "nostr://ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit",
@@ -962,6 +1054,7 @@ mod tests {
962 }, 1054 },
963 protocol: Some(ServerProtocol::Ssh), 1055 protocol: Some(ServerProtocol::Ssh),
964 user: Some("bla".to_string()), 1056 user: Some("bla".to_string()),
1057 nip05: None,
965 } 1058 }
966 ), 1059 ),
967 "nostr://bla@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit", 1060 "nostr://bla@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit",
@@ -971,8 +1064,6 @@ mod tests {
971 } 1064 }
972 1065
973 mod nostr_url_decoded_paramemters_from_str { 1066 mod nostr_url_decoded_paramemters_from_str {
974 use std::str::FromStr;
975
976 use super::*; 1067 use super::*;
977 1068
978 fn get_model_coordinate(relays: bool) -> Coordinate { 1069 fn get_model_coordinate(relays: bool) -> Coordinate {
@@ -991,11 +1082,11 @@ mod tests {
991 } 1082 }
992 } 1083 }
993 1084
994 #[test] 1085 #[tokio::test]
995 fn from_naddr() -> Result<()> { 1086 async fn from_naddr() -> Result<()> {
996 let url = "nostr://naddr1qqzxuemfwsqs6amnwvaz7tmwdaejumr0dspzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqvzqqqrhnym0k2qj".to_string(); 1087 let url = "nostr://naddr1qqzxuemfwsqs6amnwvaz7tmwdaejumr0dspzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqvzqqqrhnym0k2qj".to_string();
997 assert_eq!( 1088 assert_eq!(
998 NostrUrlDecoded::from_str(&url)?, 1089 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
999 NostrUrlDecoded { 1090 NostrUrlDecoded {
1000 original_string: url.clone(), 1091 original_string: url.clone(),
1001 coordinate: Coordinate { 1092 coordinate: Coordinate {
@@ -1010,6 +1101,7 @@ mod tests {
1010 }, 1101 },
1011 protocol: None, 1102 protocol: None,
1012 user: None, 1103 user: None,
1104 nip05: None,
1013 }, 1105 },
1014 ); 1106 );
1015 Ok(()) 1107 Ok(())
@@ -1018,18 +1110,19 @@ mod tests {
1018 mod from_npub_slash_identifier { 1110 mod from_npub_slash_identifier {
1019 use super::*; 1111 use super::*;
1020 1112
1021 #[test] 1113 #[tokio::test]
1022 fn without_relay() -> Result<()> { 1114 async fn without_relay() -> Result<()> {
1023 let url = 1115 let url =
1024 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit" 1116 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit"
1025 .to_string(); 1117 .to_string();
1026 assert_eq!( 1118 assert_eq!(
1027 NostrUrlDecoded::from_str(&url)?, 1119 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1028 NostrUrlDecoded { 1120 NostrUrlDecoded {
1029 original_string: url.clone(), 1121 original_string: url.clone(),
1030 coordinate: get_model_coordinate(false), 1122 coordinate: get_model_coordinate(false),
1031 protocol: None, 1123 protocol: None,
1032 user: None, 1124 user: None,
1125 nip05: None,
1033 }, 1126 },
1034 ); 1127 );
1035 Ok(()) 1128 Ok(())
@@ -1038,48 +1131,50 @@ mod tests {
1038 mod with_url_parameters { 1131 mod with_url_parameters {
1039 use super::*; 1132 use super::*;
1040 1133
1041 #[test] 1134 #[tokio::test]
1042 fn with_relay_without_scheme_defaults_to_wss() -> Result<()> { 1135 async fn with_relay_without_scheme_defaults_to_wss() -> Result<()> {
1043 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay=nos.lol".to_string(); 1136 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay=nos.lol".to_string();
1044 assert_eq!( 1137 assert_eq!(
1045 NostrUrlDecoded::from_str(&url)?, 1138 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1046 NostrUrlDecoded { 1139 NostrUrlDecoded {
1047 original_string: url.clone(), 1140 original_string: url.clone(),
1048 coordinate: get_model_coordinate(true), 1141 coordinate: get_model_coordinate(true),
1049 protocol: None, 1142 protocol: None,
1050 user: None, 1143 user: None,
1144 nip05: None,
1051 }, 1145 },
1052 ); 1146 );
1053 Ok(()) 1147 Ok(())
1054 } 1148 }
1055 1149
1056 #[test] 1150 #[tokio::test]
1057 fn with_encoded_relay() -> Result<()> { 1151 async fn with_encoded_relay() -> Result<()> {
1058 let url = format!( 1152 let url = format!(
1059 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}", 1153 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}",
1060 urlencoding::encode("wss://nos.lol") 1154 urlencoding::encode("wss://nos.lol")
1061 ); 1155 );
1062 assert_eq!( 1156 assert_eq!(
1063 NostrUrlDecoded::from_str(&url)?, 1157 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1064 NostrUrlDecoded { 1158 NostrUrlDecoded {
1065 original_string: url.clone(), 1159 original_string: url.clone(),
1066 coordinate: get_model_coordinate(true), 1160 coordinate: get_model_coordinate(true),
1067 protocol: None, 1161 protocol: None,
1068 user: None, 1162 user: None,
1163 nip05: None,
1069 }, 1164 },
1070 ); 1165 );
1071 Ok(()) 1166 Ok(())
1072 } 1167 }
1073 1168
1074 #[test] 1169 #[tokio::test]
1075 fn with_multiple_encoded_relays() -> Result<()> { 1170 async fn with_multiple_encoded_relays() -> Result<()> {
1076 let url = format!( 1171 let url = format!(
1077 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}&relay1={}", 1172 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}&relay1={}",
1078 urlencoding::encode("wss://nos.lol/"), 1173 urlencoding::encode("wss://nos.lol/"),
1079 urlencoding::encode("wss://relay.damus.io/"), 1174 urlencoding::encode("wss://relay.damus.io/"),
1080 ); 1175 );
1081 assert_eq!( 1176 assert_eq!(
1082 NostrUrlDecoded::from_str(&url)?, 1177 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1083 NostrUrlDecoded { 1178 NostrUrlDecoded {
1084 original_string: url.clone(), 1179 original_string: url.clone(),
1085 coordinate: Coordinate { 1180 coordinate: Coordinate {
@@ -1096,36 +1191,39 @@ mod tests {
1096 }, 1191 },
1097 protocol: None, 1192 protocol: None,
1098 user: None, 1193 user: None,
1194 nip05: None,
1099 }, 1195 },
1100 ); 1196 );
1101 Ok(()) 1197 Ok(())
1102 } 1198 }
1103 1199
1104 #[test] 1200 #[tokio::test]
1105 fn with_server_protocol() -> Result<()> { 1201 async fn with_server_protocol() -> Result<()> {
1106 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh".to_string(); 1202 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh".to_string();
1107 assert_eq!( 1203 assert_eq!(
1108 NostrUrlDecoded::from_str(&url)?, 1204 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1109 NostrUrlDecoded { 1205 NostrUrlDecoded {
1110 original_string: url.clone(), 1206 original_string: url.clone(),
1111 coordinate: get_model_coordinate(false), 1207 coordinate: get_model_coordinate(false),
1112 protocol: Some(ServerProtocol::Ssh), 1208 protocol: Some(ServerProtocol::Ssh),
1113 user: None, 1209 user: None,
1210 nip05: None,
1114 }, 1211 },
1115 ); 1212 );
1116 Ok(()) 1213 Ok(())
1117 } 1214 }
1118 1215
1119 #[test] 1216 #[tokio::test]
1120 fn with_server_protocol_and_user() -> Result<()> { 1217 async fn with_server_protocol_and_user() -> Result<()> {
1121 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh&user=fred".to_string(); 1218 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh&user=fred".to_string();
1122 assert_eq!( 1219 assert_eq!(
1123 NostrUrlDecoded::from_str(&url)?, 1220 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1124 NostrUrlDecoded { 1221 NostrUrlDecoded {
1125 original_string: url.clone(), 1222 original_string: url.clone(),
1126 coordinate: get_model_coordinate(false), 1223 coordinate: get_model_coordinate(false),
1127 protocol: Some(ServerProtocol::Ssh), 1224 protocol: Some(ServerProtocol::Ssh),
1128 user: Some("fred".to_string()), 1225 user: Some("fred".to_string()),
1226 nip05: None,
1129 }, 1227 },
1130 ); 1228 );
1131 Ok(()) 1229 Ok(())
@@ -1135,48 +1233,50 @@ mod tests {
1135 mod with_parameters_embedded_with_slashes { 1233 mod with_parameters_embedded_with_slashes {
1136 use super::*; 1234 use super::*;
1137 1235
1138 #[test] 1236 #[tokio::test]
1139 fn with_relay_without_scheme_defaults_to_wss() -> Result<()> { 1237 async fn with_relay_without_scheme_defaults_to_wss() -> Result<()> {
1140 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit".to_string(); 1238 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit".to_string();
1141 assert_eq!( 1239 assert_eq!(
1142 NostrUrlDecoded::from_str(&url)?, 1240 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1143 NostrUrlDecoded { 1241 NostrUrlDecoded {
1144 original_string: url.clone(), 1242 original_string: url.clone(),
1145 coordinate: get_model_coordinate(true), 1243 coordinate: get_model_coordinate(true),
1146 protocol: None, 1244 protocol: None,
1147 user: None, 1245 user: None,
1246 nip05: None,
1148 }, 1247 },
1149 ); 1248 );
1150 Ok(()) 1249 Ok(())
1151 } 1250 }
1152 1251
1153 #[test] 1252 #[tokio::test]
1154 fn with_encoded_relay() -> Result<()> { 1253 async fn with_encoded_relay() -> Result<()> {
1155 let url = format!( 1254 let url = format!(
1156 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/ngit", 1255 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/ngit",
1157 urlencoding::encode("wss://nos.lol") 1256 urlencoding::encode("wss://nos.lol")
1158 ); 1257 );
1159 assert_eq!( 1258 assert_eq!(
1160 NostrUrlDecoded::from_str(&url)?, 1259 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1161 NostrUrlDecoded { 1260 NostrUrlDecoded {
1162 original_string: url.clone(), 1261 original_string: url.clone(),
1163 coordinate: get_model_coordinate(true), 1262 coordinate: get_model_coordinate(true),
1164 protocol: None, 1263 protocol: None,
1165 user: None, 1264 user: None,
1265 nip05: None,
1166 }, 1266 },
1167 ); 1267 );
1168 Ok(()) 1268 Ok(())
1169 } 1269 }
1170 1270
1171 #[test] 1271 #[tokio::test]
1172 fn with_multiple_encoded_relays() -> Result<()> { 1272 async fn with_multiple_encoded_relays() -> Result<()> {
1173 let url = format!( 1273 let url = format!(
1174 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/{}/ngit", 1274 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/{}/ngit",
1175 urlencoding::encode("wss://nos.lol/"), 1275 urlencoding::encode("wss://nos.lol/"),
1176 urlencoding::encode("wss://relay.damus.io/"), 1276 urlencoding::encode("wss://relay.damus.io/"),
1177 ); 1277 );
1178 assert_eq!( 1278 assert_eq!(
1179 NostrUrlDecoded::from_str(&url)?, 1279 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1180 NostrUrlDecoded { 1280 NostrUrlDecoded {
1181 original_string: url.clone(), 1281 original_string: url.clone(),
1182 coordinate: Coordinate { 1282 coordinate: Coordinate {
@@ -1193,36 +1293,39 @@ mod tests {
1193 }, 1293 },
1194 protocol: None, 1294 protocol: None,
1195 user: None, 1295 user: None,
1296 nip05: None,
1196 }, 1297 },
1197 ); 1298 );
1198 Ok(()) 1299 Ok(())
1199 } 1300 }
1200 1301
1201 #[test] 1302 #[tokio::test]
1202 fn with_server_protocol() -> Result<()> { 1303 async fn with_server_protocol() -> Result<()> {
1203 let url = "nostr://ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit".to_string(); 1304 let url = "nostr://ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit".to_string();
1204 assert_eq!( 1305 assert_eq!(
1205 NostrUrlDecoded::from_str(&url)?, 1306 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1206 NostrUrlDecoded { 1307 NostrUrlDecoded {
1207 original_string: url.clone(), 1308 original_string: url.clone(),
1208 coordinate: get_model_coordinate(false), 1309 coordinate: get_model_coordinate(false),
1209 protocol: Some(ServerProtocol::Ssh), 1310 protocol: Some(ServerProtocol::Ssh),
1210 user: None, 1311 user: None,
1312 nip05: None,
1211 }, 1313 },
1212 ); 1314 );
1213 Ok(()) 1315 Ok(())
1214 } 1316 }
1215 1317
1216 #[test] 1318 #[tokio::test]
1217 fn with_server_protocol_and_user() -> Result<()> { 1319 async fn with_server_protocol_and_user() -> Result<()> {
1218 let url = "nostr://fred@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit".to_string(); 1320 let url = "nostr://fred@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit".to_string();
1219 assert_eq!( 1321 assert_eq!(
1220 NostrUrlDecoded::from_str(&url)?, 1322 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1221 NostrUrlDecoded { 1323 NostrUrlDecoded {
1222 original_string: url.clone(), 1324 original_string: url.clone(),
1223 coordinate: get_model_coordinate(false), 1325 coordinate: get_model_coordinate(false),
1224 protocol: Some(ServerProtocol::Ssh), 1326 protocol: Some(ServerProtocol::Ssh),
1225 user: Some("fred".to_string()), 1327 user: Some("fred".to_string()),
1328 nip05: None,
1226 }, 1329 },
1227 ); 1330 );
1228 Ok(()) 1331 Ok(())