diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2024-09-04 11:32:05 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2024-09-04 14:23:54 +0100 |
| commit | 771f944af447c202eba045936a36dee71ab797ac (patch) | |
| tree | e691de4ebc8dde7ac4855e139881ff923bc254ce /src/lib/git/nostr_url.rs | |
| parent | 949c6459aa7683453a7160423b689ceadb08954b (diff) | |
refactor: fix imports, etc based on restructure
move some functions out of ngit and into lib/mod
and lib/git_events
remove MockConnect from binaries so it is only used in the library.
this was done:
* mainly because automocks were not being imported from
lib into each binary
* but also because the these functions were being
tested with MockConnect
Diffstat (limited to 'src/lib/git/nostr_url.rs')
| -rw-r--r-- | src/lib/git/nostr_url.rs | 501 |
1 files changed, 501 insertions, 0 deletions
diff --git a/src/lib/git/nostr_url.rs b/src/lib/git/nostr_url.rs new file mode 100644 index 0000000..ce3e973 --- /dev/null +++ b/src/lib/git/nostr_url.rs | |||
| @@ -0,0 +1,501 @@ | |||
| 1 | use std::collections::HashSet; | ||
| 2 | |||
| 3 | use anyhow::{bail, Context, Result}; | ||
| 4 | use nostr::nips::nip01::Coordinate; | ||
| 5 | use nostr_sdk::{PublicKey, Url}; | ||
| 6 | |||
| 7 | #[derive(Debug, PartialEq)] | ||
| 8 | pub enum ServerProtocol { | ||
| 9 | Ssh, | ||
| 10 | Https, | ||
| 11 | Http, | ||
| 12 | Git, | ||
| 13 | } | ||
| 14 | |||
| 15 | #[derive(Debug, PartialEq)] | ||
| 16 | pub struct NostrUrlDecoded { | ||
| 17 | pub coordinates: HashSet<Coordinate>, | ||
| 18 | pub protocol: Option<ServerProtocol>, | ||
| 19 | pub user: Option<String>, | ||
| 20 | } | ||
| 21 | |||
| 22 | 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"; | ||
| 23 | |||
| 24 | impl NostrUrlDecoded { | ||
| 25 | pub fn from_str(url: &str) -> Result<Self> { | ||
| 26 | let mut coordinates = HashSet::new(); | ||
| 27 | let mut protocol = None; | ||
| 28 | let mut user = None; | ||
| 29 | let mut relays = vec![]; | ||
| 30 | |||
| 31 | if !url.starts_with("nostr://") { | ||
| 32 | bail!("nostr git url must start with nostr://"); | ||
| 33 | } | ||
| 34 | // process get url parameters if present | ||
| 35 | for (name, value) in Url::parse(url)?.query_pairs() { | ||
| 36 | if name.contains("relay") { | ||
| 37 | let mut decoded = urlencoding::decode(&value) | ||
| 38 | .context("could not parse relays in nostr git url")? | ||
| 39 | .to_string(); | ||
| 40 | if !decoded.starts_with("ws://") && !decoded.starts_with("wss://") { | ||
| 41 | decoded = format!("wss://{decoded}"); | ||
| 42 | } | ||
| 43 | let url = | ||
| 44 | Url::parse(&decoded).context("could not parse relays in nostr git url")?; | ||
| 45 | relays.push(url.to_string()); | ||
| 46 | } else if name == "protocol" { | ||
| 47 | protocol = match value.as_ref() { | ||
| 48 | "ssh" => Some(ServerProtocol::Ssh), | ||
| 49 | "https" => Some(ServerProtocol::Https), | ||
| 50 | "http" => Some(ServerProtocol::Http), | ||
| 51 | "git" => Some(ServerProtocol::Git), | ||
| 52 | _ => None, | ||
| 53 | }; | ||
| 54 | } else if name == "user" { | ||
| 55 | user = Some(value.to_string()); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | let mut parts: Vec<&str> = url[8..] | ||
| 60 | .split('?') | ||
| 61 | .next() | ||
| 62 | .unwrap_or("") | ||
| 63 | .split('/') | ||
| 64 | .collect(); | ||
| 65 | |||
| 66 | // extract optional protocol | ||
| 67 | if protocol.is_none() { | ||
| 68 | let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?; | ||
| 69 | let protocol_str = if let Some(at_index) = part.find('@') { | ||
| 70 | user = Some(part[..at_index].to_string()); | ||
| 71 | &part[at_index + 1..] | ||
| 72 | } else { | ||
| 73 | part | ||
| 74 | }; | ||
| 75 | protocol = match protocol_str { | ||
| 76 | "ssh" => Some(ServerProtocol::Ssh), | ||
| 77 | "https" => Some(ServerProtocol::Https), | ||
| 78 | "http" => Some(ServerProtocol::Http), | ||
| 79 | "git" => Some(ServerProtocol::Git), | ||
| 80 | _ => protocol, | ||
| 81 | }; | ||
| 82 | if protocol.is_some() { | ||
| 83 | parts.remove(0); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | // extract naddr npub/<optional-relays>/identifer | ||
| 87 | let part = parts.first().context(INCORRECT_NOSTR_URL_FORMAT_ERROR)?; | ||
| 88 | // naddr used | ||
| 89 | if let Ok(coordinate) = Coordinate::parse(part) { | ||
| 90 | if coordinate.kind.eq(&nostr_sdk::Kind::GitRepoAnnouncement) { | ||
| 91 | coordinates.insert(coordinate); | ||
| 92 | } else { | ||
| 93 | bail!("naddr doesnt point to a git repository announcement"); | ||
| 94 | } | ||
| 95 | // npub/<optional-relays>/identifer used | ||
| 96 | } else if let Ok(public_key) = PublicKey::parse(part) { | ||
| 97 | parts.remove(0); | ||
| 98 | let identifier = parts | ||
| 99 | .pop() | ||
| 100 | .context("nostr url must have an identifier eg. nostr://npub123/repo-identifier")? | ||
| 101 | .to_string(); | ||
| 102 | for relay in parts { | ||
| 103 | let mut decoded = urlencoding::decode(relay) | ||
| 104 | .context("could not parse relays in nostr git url")? | ||
| 105 | .to_string(); | ||
| 106 | if !decoded.starts_with("ws://") && !decoded.starts_with("wss://") { | ||
| 107 | decoded = format!("wss://{decoded}"); | ||
| 108 | } | ||
| 109 | let url = | ||
| 110 | Url::parse(&decoded).context("could not parse relays in nostr git url")?; | ||
| 111 | relays.push(url.to_string()); | ||
| 112 | } | ||
| 113 | coordinates.insert(Coordinate { | ||
| 114 | identifier, | ||
| 115 | public_key, | ||
| 116 | kind: nostr_sdk::Kind::GitRepoAnnouncement, | ||
| 117 | relays, | ||
| 118 | }); | ||
| 119 | } else { | ||
| 120 | bail!(INCORRECT_NOSTR_URL_FORMAT_ERROR); | ||
| 121 | } | ||
| 122 | |||
| 123 | Ok(Self { | ||
| 124 | coordinates, | ||
| 125 | protocol, | ||
| 126 | user, | ||
| 127 | }) | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | /** produce error when using local repo or custom protocols */ | ||
| 132 | pub fn convert_clone_url_to_https(url: &str) -> Result<String> { | ||
| 133 | // Strip credentials if present | ||
| 134 | let stripped_url = strip_credentials(url); | ||
| 135 | |||
| 136 | // Check if the URL is already in HTTPS format | ||
| 137 | if stripped_url.starts_with("https://") { | ||
| 138 | return Ok(stripped_url); | ||
| 139 | } | ||
| 140 | // Convert http:// to https:// | ||
| 141 | else if stripped_url.starts_with("http://") { | ||
| 142 | return Ok(stripped_url.replace("http://", "https://")); | ||
| 143 | } | ||
| 144 | // Check if the URL starts with SSH | ||
| 145 | else if stripped_url.starts_with("ssh://") { | ||
| 146 | // Convert SSH to HTTPS | ||
| 147 | let parts: Vec<&str> = stripped_url | ||
| 148 | .trim_start_matches("ssh://") | ||
| 149 | .split('/') | ||
| 150 | .collect(); | ||
| 151 | if parts.len() >= 2 { | ||
| 152 | // Construct the HTTPS URL | ||
| 153 | return Ok(format!("https://{}/{}", parts[0], parts[1..].join("/"))); | ||
| 154 | } | ||
| 155 | bail!("Invalid SSH URL format: {}", url); | ||
| 156 | } | ||
| 157 | // Convert ftp:// to https:// | ||
| 158 | else if stripped_url.starts_with("ftp://") { | ||
| 159 | return Ok(stripped_url.replace("ftp://", "https://")); | ||
| 160 | } | ||
| 161 | // Convert git:// to https:// | ||
| 162 | else if stripped_url.starts_with("git://") { | ||
| 163 | return Ok(stripped_url.replace("git://", "https://")); | ||
| 164 | } | ||
| 165 | |||
| 166 | // If the URL is neither HTTPS, SSH, nor git@, return an error | ||
| 167 | bail!("Unsupported URL protocol: {}", url); | ||
| 168 | } | ||
| 169 | |||
| 170 | // Function to strip username and password from the URL | ||
| 171 | fn strip_credentials(url: &str) -> String { | ||
| 172 | if let Some(pos) = url.find("://") { | ||
| 173 | let (protocol, rest) = url.split_at(pos + 3); // Split at "://" | ||
| 174 | let rest_parts: Vec<&str> = rest.split('@').collect(); | ||
| 175 | if rest_parts.len() > 1 { | ||
| 176 | // If there are credentials, return the URL without them | ||
| 177 | return format!("{}{}", protocol, rest_parts[1]); | ||
| 178 | } | ||
| 179 | } else if let Some(at_pos) = url.find('@') { | ||
| 180 | // Handle user@host:path format | ||
| 181 | let (_, rest) = url.split_at(at_pos); | ||
| 182 | // This is a git@ syntax | ||
| 183 | let host_and_repo = &rest[1..]; // Skip the ':' | ||
| 184 | return format!("ssh://{}", host_and_repo.replace(':', "/")); | ||
| 185 | } | ||
| 186 | url.to_string() // Return the original URL if no credentials are found | ||
| 187 | } | ||
| 188 | |||
| 189 | #[cfg(test)] | ||
| 190 | mod tests { | ||
| 191 | use super::*; | ||
| 192 | mod convert_clone_url_to_https { | ||
| 193 | use super::*; | ||
| 194 | |||
| 195 | #[test] | ||
| 196 | fn test_https_url() { | ||
| 197 | let url = "https://github.com/user/repo.git"; | ||
| 198 | let result = convert_clone_url_to_https(url).unwrap(); | ||
| 199 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 200 | } | ||
| 201 | |||
| 202 | #[test] | ||
| 203 | fn test_http_url() { | ||
| 204 | let url = "http://github.com/user/repo.git"; | ||
| 205 | let result = convert_clone_url_to_https(url).unwrap(); | ||
| 206 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 207 | } | ||
| 208 | |||
| 209 | #[test] | ||
| 210 | fn test_http_url_with_credentials() { | ||
| 211 | let url = "http://username:password@github.com/user/repo.git"; | ||
| 212 | let result = convert_clone_url_to_https(url).unwrap(); | ||
| 213 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 214 | } | ||
| 215 | |||
| 216 | #[test] | ||
| 217 | fn test_git_at_url() { | ||
| 218 | let url = "git@github.com:user/repo.git"; | ||
| 219 | let result = convert_clone_url_to_https(url).unwrap(); | ||
| 220 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 221 | } | ||
| 222 | |||
| 223 | #[test] | ||
| 224 | fn test_user_at_url() { | ||
| 225 | let url = "user1@github.com:user/repo.git"; | ||
| 226 | let result = convert_clone_url_to_https(url).unwrap(); | ||
| 227 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 228 | } | ||
| 229 | |||
| 230 | #[test] | ||
| 231 | fn test_ssh_url() { | ||
| 232 | let url = "ssh://github.com/user/repo.git"; | ||
| 233 | let result = convert_clone_url_to_https(url).unwrap(); | ||
| 234 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 235 | } | ||
| 236 | |||
| 237 | #[test] | ||
| 238 | fn test_ftp_url() { | ||
| 239 | let url = "ftp://example.com/repo.git"; | ||
| 240 | let result = convert_clone_url_to_https(url).unwrap(); | ||
| 241 | assert_eq!(result, "https://example.com/repo.git"); | ||
| 242 | } | ||
| 243 | |||
| 244 | #[test] | ||
| 245 | fn test_git_protocol_url() { | ||
| 246 | let url = "git://example.com/repo.git"; | ||
| 247 | let result = convert_clone_url_to_https(url).unwrap(); | ||
| 248 | assert_eq!(result, "https://example.com/repo.git"); | ||
| 249 | } | ||
| 250 | |||
| 251 | #[test] | ||
| 252 | fn test_invalid_url() { | ||
| 253 | let url = "unsupported://example.com/repo.git"; | ||
| 254 | let result = convert_clone_url_to_https(url); | ||
| 255 | assert!(result.is_err()); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | mod nostr_git_url_paramemters_from_str { | ||
| 260 | use super::*; | ||
| 261 | |||
| 262 | fn get_model_coordinate(relays: bool) -> Coordinate { | ||
| 263 | Coordinate { | ||
| 264 | identifier: "ngit".to_string(), | ||
| 265 | public_key: PublicKey::parse( | ||
| 266 | "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", | ||
| 267 | ) | ||
| 268 | .unwrap(), | ||
| 269 | kind: nostr_sdk::Kind::GitRepoAnnouncement, | ||
| 270 | relays: if relays { | ||
| 271 | vec!["wss://nos.lol/".to_string()] | ||
| 272 | } else { | ||
| 273 | vec![] | ||
| 274 | }, | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | #[test] | ||
| 279 | fn from_naddr() -> Result<()> { | ||
| 280 | assert_eq!( | ||
| 281 | NostrUrlDecoded::from_str( | ||
| 282 | "nostr://naddr1qqzxuemfwsqs6amnwvaz7tmwdaejumr0dspzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqvzqqqrhnym0k2qj" | ||
| 283 | )?, | ||
| 284 | NostrUrlDecoded { | ||
| 285 | coordinates: HashSet::from([Coordinate { | ||
| 286 | identifier: "ngit".to_string(), | ||
| 287 | public_key: PublicKey::parse( | ||
| 288 | "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", | ||
| 289 | ) | ||
| 290 | .unwrap(), | ||
| 291 | kind: nostr_sdk::Kind::GitRepoAnnouncement, | ||
| 292 | relays: vec!["wss://nos.lol".to_string()], // wont add the slash | ||
| 293 | }]), | ||
| 294 | protocol: None, | ||
| 295 | user: None, | ||
| 296 | }, | ||
| 297 | ); | ||
| 298 | Ok(()) | ||
| 299 | } | ||
| 300 | mod from_npub_slash_identifier { | ||
| 301 | use super::*; | ||
| 302 | |||
| 303 | #[test] | ||
| 304 | fn without_relay() -> Result<()> { | ||
| 305 | assert_eq!( | ||
| 306 | NostrUrlDecoded::from_str( | ||
| 307 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit" | ||
| 308 | )?, | ||
| 309 | NostrUrlDecoded { | ||
| 310 | coordinates: HashSet::from([get_model_coordinate(false)]), | ||
| 311 | protocol: None, | ||
| 312 | user: None, | ||
| 313 | }, | ||
| 314 | ); | ||
| 315 | Ok(()) | ||
| 316 | } | ||
| 317 | |||
| 318 | mod with_url_parameters { | ||
| 319 | |||
| 320 | use super::*; | ||
| 321 | |||
| 322 | #[test] | ||
| 323 | fn with_relay_without_scheme_defaults_to_wss() -> Result<()> { | ||
| 324 | assert_eq!( | ||
| 325 | NostrUrlDecoded::from_str( | ||
| 326 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay=nos.lol" | ||
| 327 | )?, | ||
| 328 | NostrUrlDecoded { | ||
| 329 | coordinates: HashSet::from([get_model_coordinate(true)]), | ||
| 330 | protocol: None, | ||
| 331 | user: None, | ||
| 332 | }, | ||
| 333 | ); | ||
| 334 | Ok(()) | ||
| 335 | } | ||
| 336 | |||
| 337 | #[test] | ||
| 338 | fn with_encoded_relay() -> Result<()> { | ||
| 339 | assert_eq!( | ||
| 340 | NostrUrlDecoded::from_str(&format!( | ||
| 341 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}", | ||
| 342 | urlencoding::encode("wss://nos.lol") | ||
| 343 | ))?, | ||
| 344 | NostrUrlDecoded { | ||
| 345 | coordinates: HashSet::from([get_model_coordinate(true)]), | ||
| 346 | protocol: None, | ||
| 347 | user: None, | ||
| 348 | }, | ||
| 349 | ); | ||
| 350 | Ok(()) | ||
| 351 | } | ||
| 352 | #[test] | ||
| 353 | fn with_multiple_encoded_relays() -> Result<()> { | ||
| 354 | assert_eq!( | ||
| 355 | NostrUrlDecoded::from_str(&format!( | ||
| 356 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?relay={}&relay1={}", | ||
| 357 | urlencoding::encode("wss://nos.lol"), | ||
| 358 | urlencoding::encode("wss://relay.damus.io"), | ||
| 359 | ))?, | ||
| 360 | NostrUrlDecoded { | ||
| 361 | coordinates: HashSet::from([Coordinate { | ||
| 362 | identifier: "ngit".to_string(), | ||
| 363 | public_key: PublicKey::parse( | ||
| 364 | "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", | ||
| 365 | ) | ||
| 366 | .unwrap(), | ||
| 367 | kind: nostr_sdk::Kind::GitRepoAnnouncement, | ||
| 368 | relays: vec![ | ||
| 369 | "wss://nos.lol/".to_string(), | ||
| 370 | "wss://relay.damus.io/".to_string(), | ||
| 371 | ], | ||
| 372 | }]), | ||
| 373 | protocol: None, | ||
| 374 | user: None, | ||
| 375 | }, | ||
| 376 | ); | ||
| 377 | Ok(()) | ||
| 378 | } | ||
| 379 | |||
| 380 | #[test] | ||
| 381 | fn with_server_protocol() -> Result<()> { | ||
| 382 | assert_eq!( | ||
| 383 | NostrUrlDecoded::from_str( | ||
| 384 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh" | ||
| 385 | )?, | ||
| 386 | NostrUrlDecoded { | ||
| 387 | coordinates: HashSet::from([get_model_coordinate(false)]), | ||
| 388 | protocol: Some(ServerProtocol::Ssh), | ||
| 389 | user: None, | ||
| 390 | }, | ||
| 391 | ); | ||
| 392 | Ok(()) | ||
| 393 | } | ||
| 394 | #[test] | ||
| 395 | fn with_server_protocol_and_user() -> Result<()> { | ||
| 396 | assert_eq!( | ||
| 397 | NostrUrlDecoded::from_str( | ||
| 398 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh&user=fred" | ||
| 399 | )?, | ||
| 400 | NostrUrlDecoded { | ||
| 401 | coordinates: HashSet::from([get_model_coordinate(false)]), | ||
| 402 | protocol: Some(ServerProtocol::Ssh), | ||
| 403 | user: Some("fred".to_string()), | ||
| 404 | }, | ||
| 405 | ); | ||
| 406 | Ok(()) | ||
| 407 | } | ||
| 408 | } | ||
| 409 | mod with_parameters_embedded_with_slashes { | ||
| 410 | use super::*; | ||
| 411 | |||
| 412 | #[test] | ||
| 413 | fn with_relay_without_scheme_defaults_to_wss() -> Result<()> { | ||
| 414 | assert_eq!( | ||
| 415 | NostrUrlDecoded::from_str( | ||
| 416 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit" | ||
| 417 | )?, | ||
| 418 | NostrUrlDecoded { | ||
| 419 | coordinates: HashSet::from([get_model_coordinate(true)]), | ||
| 420 | protocol: None, | ||
| 421 | user: None, | ||
| 422 | }, | ||
| 423 | ); | ||
| 424 | Ok(()) | ||
| 425 | } | ||
| 426 | |||
| 427 | #[test] | ||
| 428 | fn with_encoded_relay() -> Result<()> { | ||
| 429 | assert_eq!( | ||
| 430 | NostrUrlDecoded::from_str(&format!( | ||
| 431 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/ngit", | ||
| 432 | urlencoding::encode("wss://nos.lol") | ||
| 433 | ))?, | ||
| 434 | NostrUrlDecoded { | ||
| 435 | coordinates: HashSet::from([get_model_coordinate(true)]), | ||
| 436 | protocol: None, | ||
| 437 | user: None, | ||
| 438 | }, | ||
| 439 | ); | ||
| 440 | Ok(()) | ||
| 441 | } | ||
| 442 | #[test] | ||
| 443 | fn with_multiple_encoded_relays() -> Result<()> { | ||
| 444 | assert_eq!( | ||
| 445 | NostrUrlDecoded::from_str(&format!( | ||
| 446 | "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/{}/{}/ngit", | ||
| 447 | urlencoding::encode("wss://nos.lol"), | ||
| 448 | urlencoding::encode("wss://relay.damus.io"), | ||
| 449 | ))?, | ||
| 450 | NostrUrlDecoded { | ||
| 451 | coordinates: HashSet::from([Coordinate { | ||
| 452 | identifier: "ngit".to_string(), | ||
| 453 | public_key: PublicKey::parse( | ||
| 454 | "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr", | ||
| 455 | ) | ||
| 456 | .unwrap(), | ||
| 457 | kind: nostr_sdk::Kind::GitRepoAnnouncement, | ||
| 458 | relays: vec![ | ||
| 459 | "wss://nos.lol/".to_string(), | ||
| 460 | "wss://relay.damus.io/".to_string(), | ||
| 461 | ], | ||
| 462 | }]), | ||
| 463 | protocol: None, | ||
| 464 | user: None, | ||
| 465 | }, | ||
| 466 | ); | ||
| 467 | Ok(()) | ||
| 468 | } | ||
| 469 | |||
| 470 | #[test] | ||
| 471 | fn with_server_protocol() -> Result<()> { | ||
| 472 | assert_eq!( | ||
| 473 | NostrUrlDecoded::from_str( | ||
| 474 | "nostr://ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit" | ||
| 475 | )?, | ||
| 476 | NostrUrlDecoded { | ||
| 477 | coordinates: HashSet::from([get_model_coordinate(false)]), | ||
| 478 | protocol: Some(ServerProtocol::Ssh), | ||
| 479 | user: None, | ||
| 480 | }, | ||
| 481 | ); | ||
| 482 | Ok(()) | ||
| 483 | } | ||
| 484 | #[test] | ||
| 485 | fn with_server_protocol_and_user() -> Result<()> { | ||
| 486 | assert_eq!( | ||
| 487 | NostrUrlDecoded::from_str( | ||
| 488 | "nostr://fred@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit" | ||
| 489 | )?, | ||
| 490 | NostrUrlDecoded { | ||
| 491 | coordinates: HashSet::from([get_model_coordinate(false)]), | ||
| 492 | protocol: Some(ServerProtocol::Ssh), | ||
| 493 | user: Some("fred".to_string()), | ||
| 494 | }, | ||
| 495 | ); | ||
| 496 | Ok(()) | ||
| 497 | } | ||
| 498 | } | ||
| 499 | } | ||
| 500 | } | ||
| 501 | } | ||