diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2024-09-06 07:43:13 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2024-09-06 07:43:13 +0100 |
| commit | fad3f8fddffb55597432243e129e4012034b3627 (patch) | |
| tree | fc4b3d07a1cebc020d5a945956a95722cbd2852f /src | |
| parent | c4c262a5e9bfeb30bc0106d9ea51dfce7e4fa1f3 (diff) | |
add `CloneUrl`
to enable remote to interact with git servers over a range of
specified protocols
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/git/nostr_url.rs | 547 |
1 files changed, 544 insertions, 3 deletions
diff --git a/src/lib/git/nostr_url.rs b/src/lib/git/nostr_url.rs index d1fee2e..f46e751 100644 --- a/src/lib/git/nostr_url.rs +++ b/src/lib/git/nostr_url.rs | |||
| @@ -1,15 +1,33 @@ | |||
| 1 | use std::collections::HashSet; | 1 | use core::fmt; |
| 2 | use std::{collections::HashSet, str::FromStr}; | ||
| 2 | 3 | ||
| 3 | use anyhow::{bail, Context, Result}; | 4 | use anyhow::{anyhow, bail, Context, Result}; |
| 4 | use nostr::nips::nip01::Coordinate; | 5 | use nostr::nips::nip01::Coordinate; |
| 5 | use nostr_sdk::{PublicKey, Url}; | 6 | use nostr_sdk::{PublicKey, Url}; |
| 6 | 7 | ||
| 7 | #[derive(Debug, PartialEq)] | 8 | #[derive(Debug, PartialEq, Default, Clone)] |
| 8 | pub enum ServerProtocol { | 9 | pub enum ServerProtocol { |
| 9 | Ssh, | 10 | Ssh, |
| 10 | Https, | 11 | Https, |
| 11 | Http, | 12 | Http, |
| 12 | Git, | 13 | Git, |
| 14 | Ftp, | ||
| 15 | Local, | ||
| 16 | #[default] | ||
| 17 | Unspecified, | ||
| 18 | } | ||
| 19 | impl fmt::Display for ServerProtocol { | ||
| 20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
| 21 | match self { | ||
| 22 | ServerProtocol::Http => write!(f, "HTTP"), | ||
| 23 | ServerProtocol::Https => write!(f, "HTTPS"), | ||
| 24 | ServerProtocol::Ftp => write!(f, "FTP"), | ||
| 25 | ServerProtocol::Ssh => write!(f, "SSH"), | ||
| 26 | ServerProtocol::Git => write!(f, "GIT"), | ||
| 27 | ServerProtocol::Local => write!(f, "LOCAL"), | ||
| 28 | ServerProtocol::Unspecified => write!(f, "Unsepcified"), | ||
| 29 | } | ||
| 30 | } | ||
| 13 | } | 31 | } |
| 14 | 32 | ||
| 15 | #[derive(Debug, PartialEq)] | 33 | #[derive(Debug, PartialEq)] |
| @@ -130,6 +148,185 @@ impl std::str::FromStr for NostrUrlDecoded { | |||
| 130 | } | 148 | } |
| 131 | } | 149 | } |
| 132 | 150 | ||
| 151 | #[derive(Debug, PartialEq, Default)] | ||
| 152 | pub struct CloneUrl { | ||
| 153 | original_string: String, | ||
| 154 | host: String, | ||
| 155 | path: String, | ||
| 156 | parameters: Option<String>, | ||
| 157 | protocol: ServerProtocol, | ||
| 158 | user: Option<String>, | ||
| 159 | port: Option<u16>, | ||
| 160 | fragment: Option<String>, | ||
| 161 | } | ||
| 162 | |||
| 163 | impl FromStr for CloneUrl { | ||
| 164 | type Err = anyhow::Error; | ||
| 165 | |||
| 166 | fn from_str(s: &str) -> Result<Self> { | ||
| 167 | // Check if the input is a local path | ||
| 168 | if s.starts_with('/') || s.starts_with("./") || s.starts_with("../") { | ||
| 169 | return Ok(Self { | ||
| 170 | original_string: s.to_string(), | ||
| 171 | protocol: ServerProtocol::Local, | ||
| 172 | ..CloneUrl::default() | ||
| 173 | }); | ||
| 174 | } | ||
| 175 | let url_str = if s.contains("://") { | ||
| 176 | s.to_string() // Use the original string | ||
| 177 | } else { | ||
| 178 | let protocol = // Check for the SSH format user@host:path and convert to ssh:// | ||
| 179 | if s.contains('@') && s | ||
| 180 | .split('@') | ||
| 181 | .nth(0) | ||
| 182 | .map_or(false, |part| !part.contains('/')) { | ||
| 183 | "ssh" | ||
| 184 | } | ||
| 185 | // otherwise assume unspecified | ||
| 186 | else { | ||
| 187 | "unspecified" | ||
| 188 | }; | ||
| 189 | format!( | ||
| 190 | "{protocol}://{}", | ||
| 191 | if contains_port(s) { | ||
| 192 | s.to_string() | ||
| 193 | } else { | ||
| 194 | s.replace(":/", "/").replace(':', "/") | ||
| 195 | } | ||
| 196 | ) | ||
| 197 | }; | ||
| 198 | |||
| 199 | let url = Url::parse(&url_str).context("Failed to parse URL")?; | ||
| 200 | |||
| 201 | let protocol = match url.scheme() { | ||
| 202 | "ssh" => ServerProtocol::Ssh, | ||
| 203 | "https" => ServerProtocol::Https, | ||
| 204 | "http" => ServerProtocol::Http, | ||
| 205 | "git" => ServerProtocol::Git, | ||
| 206 | "ftp" => ServerProtocol::Ftp, | ||
| 207 | "unspecified" => ServerProtocol::Unspecified, | ||
| 208 | _ => return Err(anyhow::anyhow!("Unsupported protocol: {}", url.scheme())), | ||
| 209 | }; | ||
| 210 | |||
| 211 | let host = url.host_str().context("Missing host")?.to_string(); | ||
| 212 | let path = url.path().to_string(); | ||
| 213 | let parameters = url.query().map(|s| s.to_string()); | ||
| 214 | let port = url.port(); | ||
| 215 | |||
| 216 | let fragment = url.fragment().map(|s| s.to_string()); | ||
| 217 | |||
| 218 | let user = if url.username().is_empty() { | ||
| 219 | None | ||
| 220 | } else { | ||
| 221 | Some(url.username().to_string()) | ||
| 222 | }; | ||
| 223 | |||
| 224 | Ok(CloneUrl { | ||
| 225 | original_string: s.to_string(), | ||
| 226 | host, | ||
| 227 | path, | ||
| 228 | parameters, | ||
| 229 | protocol, | ||
| 230 | user, | ||
| 231 | port, | ||
| 232 | fragment, | ||
| 233 | }) | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | fn contains_port(s: &str) -> bool { | ||
| 238 | if let Some(after_host) = s.split('@').nth(1).unwrap_or(s).split(':').nth(1) { | ||
| 239 | if let Some(port) = after_host.split('/').next() { | ||
| 240 | if port.parse::<u16>().is_ok() { | ||
| 241 | return true; | ||
| 242 | } | ||
| 243 | } | ||
| 244 | } | ||
| 245 | false | ||
| 246 | } | ||
| 247 | |||
| 248 | impl CloneUrl { | ||
| 249 | pub fn format_as(&self, protocol: &ServerProtocol, user: &Option<String>) -> Result<String> { | ||
| 250 | // Check for incompatible protocol conversions | ||
| 251 | if *protocol == ServerProtocol::Local { | ||
| 252 | if self.protocol == ServerProtocol::Local { | ||
| 253 | // If converting from Local to Local, return the original string | ||
| 254 | return Ok(self.original_string.clone()); | ||
| 255 | } else { | ||
| 256 | // If converting to Local from any other protocol, return an error | ||
| 257 | bail!("Cannot convert to Local protocol from {:?}", self.protocol); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | let mut url = Url::parse(&format!( | ||
| 262 | "{}{}", | ||
| 263 | match protocol { | ||
| 264 | ServerProtocol::Https => "https://", | ||
| 265 | ServerProtocol::Http => "http://", | ||
| 266 | ServerProtocol::Git => "git://", | ||
| 267 | ServerProtocol::Ftp => "ftp://", | ||
| 268 | ServerProtocol::Ssh => "ssh://", | ||
| 269 | ServerProtocol::Unspecified => "https://", | ||
| 270 | _ => bail!("unsupported protocol"), | ||
| 271 | }, | ||
| 272 | &self.host | ||
| 273 | )) | ||
| 274 | .context("Failed to parse base URL")?; // Start with the specified scheme | ||
| 275 | |||
| 276 | url.set_path(&self.path); | ||
| 277 | |||
| 278 | // Set the port if present | ||
| 279 | if let Some(port) = self.port { | ||
| 280 | url.set_port(Some(port)) | ||
| 281 | .map_err(|_| anyhow!("cannot add port"))?; | ||
| 282 | } | ||
| 283 | |||
| 284 | // Set the query parameters if present | ||
| 285 | if let Some(ref parameters) = self.parameters { | ||
| 286 | url.set_query(Some(parameters)); | ||
| 287 | } | ||
| 288 | |||
| 289 | // Set the fragment if present | ||
| 290 | if let Some(ref fragment) = self.fragment { | ||
| 291 | url.set_fragment(Some(fragment)); | ||
| 292 | } | ||
| 293 | |||
| 294 | let mut formatted_url = url.to_string(); | ||
| 295 | |||
| 296 | if *protocol == ServerProtocol::Ssh { | ||
| 297 | formatted_url = formatted_url.replace( | ||
| 298 | "ssh://", | ||
| 299 | format!("{}@", user.as_deref().unwrap_or("git")).as_str(), | ||
| 300 | ); | ||
| 301 | if !contains_port(&formatted_url) { | ||
| 302 | formatted_url = replace_first_occurrence(&formatted_url, '/', ':'); | ||
| 303 | } | ||
| 304 | } else if *protocol == ServerProtocol::Unspecified { | ||
| 305 | formatted_url = formatted_url.replace("https://", ""); | ||
| 306 | } | ||
| 307 | |||
| 308 | Ok(strip_trailing_slash(&formatted_url)) | ||
| 309 | } | ||
| 310 | pub fn domain(&self) -> String { | ||
| 311 | self.host.to_string() | ||
| 312 | } | ||
| 313 | pub fn protocol(&self) -> ServerProtocol { | ||
| 314 | self.protocol.clone() | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | fn replace_first_occurrence(s: &str, target: char, replacement: char) -> String { | ||
| 319 | let mut result = s.to_string(); | ||
| 320 | if let Some(index) = result.find(target) { | ||
| 321 | result.replace_range(index..index + 1, &replacement.to_string()); | ||
| 322 | } | ||
| 323 | result | ||
| 324 | } | ||
| 325 | |||
| 326 | fn strip_trailing_slash(s: &str) -> String { | ||
| 327 | s.strip_suffix('/').unwrap_or(s).to_string() | ||
| 328 | } | ||
| 329 | |||
| 133 | /** produce error when using local repo or custom protocols */ | 330 | /** produce error when using local repo or custom protocols */ |
| 134 | pub fn convert_clone_url_to_https(url: &str) -> Result<String> { | 331 | pub fn convert_clone_url_to_https(url: &str) -> Result<String> { |
| 135 | // Strip credentials if present | 332 | // Strip credentials if present |
| @@ -191,6 +388,350 @@ fn strip_credentials(url: &str) -> String { | |||
| 191 | #[cfg(test)] | 388 | #[cfg(test)] |
| 192 | mod tests { | 389 | mod tests { |
| 193 | use super::*; | 390 | use super::*; |
| 391 | |||
| 392 | mod clone_url_from_str_format_as { | ||
| 393 | use super::*; | ||
| 394 | |||
| 395 | mod when_user_specified { | ||
| 396 | use super::*; | ||
| 397 | |||
| 398 | mod but_not_in_original_url { | ||
| 399 | use super::*; | ||
| 400 | |||
| 401 | #[test] | ||
| 402 | fn https_to_https_ignores_user() { | ||
| 403 | let result = "https://github.com/user/repo.git" | ||
| 404 | .parse::<CloneUrl>() | ||
| 405 | .unwrap() | ||
| 406 | .format_as(&ServerProtocol::Https, &Some("user1".to_string())) | ||
| 407 | .unwrap(); | ||
| 408 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 409 | } | ||
| 410 | #[test] | ||
| 411 | fn https_to_ssh_uses_specified_user() { | ||
| 412 | let result = "https://github.com/user/repo.git" | ||
| 413 | .parse::<CloneUrl>() | ||
| 414 | .unwrap() | ||
| 415 | .format_as(&ServerProtocol::Ssh, &Some("user1".to_string())) | ||
| 416 | .unwrap(); | ||
| 417 | assert_eq!(result, "user1@github.com:user/repo.git"); | ||
| 418 | } | ||
| 419 | } | ||
| 420 | mod and_a_different_user_in_original_url { | ||
| 421 | use super::*; | ||
| 422 | |||
| 423 | #[test] | ||
| 424 | fn ssh_uses_specified_user() { | ||
| 425 | let result = "user2@github.com/user/repo.git" | ||
| 426 | .parse::<CloneUrl>() | ||
| 427 | .unwrap() | ||
| 428 | .format_as(&ServerProtocol::Ssh, &Some("user1".to_string())) | ||
| 429 | .unwrap(); | ||
| 430 | assert_eq!(result, "user1@github.com:user/repo.git"); | ||
| 431 | } | ||
| 432 | } | ||
| 433 | } | ||
| 434 | |||
| 435 | #[test] | ||
| 436 | fn format_as_ssh_defaults_to_git_user() { | ||
| 437 | let result = "https://github.com/user/repo.git" | ||
| 438 | .parse::<CloneUrl>() | ||
| 439 | .unwrap() | ||
| 440 | .format_as(&ServerProtocol::Ssh, &None) | ||
| 441 | .unwrap(); | ||
| 442 | assert_eq!(result, "git@github.com:user/repo.git"); | ||
| 443 | } | ||
| 444 | |||
| 445 | #[test] | ||
| 446 | fn format_as_ssh_includes_port() { | ||
| 447 | let result = "https://github.com:1000/user/repo.git" | ||
| 448 | .parse::<CloneUrl>() | ||
| 449 | .unwrap() | ||
| 450 | .format_as(&ServerProtocol::Ssh, &None) | ||
| 451 | .unwrap(); | ||
| 452 | assert_eq!(result, "git@github.com:1000/user/repo.git"); | ||
| 453 | } | ||
| 454 | |||
| 455 | #[test] | ||
| 456 | fn format_as_unspecified_ommits_prefix() { | ||
| 457 | let result = "https://github.com/user/repo.git" | ||
| 458 | .parse::<CloneUrl>() | ||
| 459 | .unwrap() | ||
| 460 | .format_as(&ServerProtocol::Unspecified, &None) | ||
| 461 | .unwrap(); | ||
| 462 | assert_eq!(result, "github.com/user/repo.git"); | ||
| 463 | } | ||
| 464 | |||
| 465 | mod input_all_formats_to_from_str_and_correctly_format_as_https { | ||
| 466 | use super::*; | ||
| 467 | |||
| 468 | #[test] | ||
| 469 | fn test_https_url() { | ||
| 470 | let result = "https://github.com/user/repo.git" | ||
| 471 | .parse::<CloneUrl>() | ||
| 472 | .unwrap() | ||
| 473 | .format_as(&ServerProtocol::Https, &None) | ||
| 474 | .unwrap(); | ||
| 475 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 476 | } | ||
| 477 | |||
| 478 | mod with_unspecified_and_additional_features { | ||
| 479 | use super::*; | ||
| 480 | |||
| 481 | #[test] | ||
| 482 | fn port() { | ||
| 483 | let result = "github.com:1000/user/repo.git" | ||
| 484 | .parse::<CloneUrl>() | ||
| 485 | .unwrap() | ||
| 486 | .format_as(&ServerProtocol::Https, &None) | ||
| 487 | .unwrap(); | ||
| 488 | assert_eq!(result, "https://github.com:1000/user/repo.git"); | ||
| 489 | } | ||
| 490 | |||
| 491 | #[test] | ||
| 492 | fn colon() { | ||
| 493 | let result = "github.com:user/repo.git" | ||
| 494 | .parse::<CloneUrl>() | ||
| 495 | .unwrap() | ||
| 496 | .format_as(&ServerProtocol::Https, &None) | ||
| 497 | .unwrap(); | ||
| 498 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 499 | } | ||
| 500 | |||
| 501 | #[test] | ||
| 502 | fn path_with_fragment() { | ||
| 503 | let result = "github.com/user/repo.git#readme" | ||
| 504 | .parse::<CloneUrl>() | ||
| 505 | .unwrap() | ||
| 506 | .format_as(&ServerProtocol::Https, &None) | ||
| 507 | .unwrap(); | ||
| 508 | assert_eq!(result, "https://github.com/user/repo.git#readme"); | ||
| 509 | } | ||
| 510 | |||
| 511 | #[test] | ||
| 512 | fn path_with_parameters() { | ||
| 513 | let result = "github.com/user/repo.git?ref=main" | ||
| 514 | .parse::<CloneUrl>() | ||
| 515 | .unwrap() | ||
| 516 | .format_as(&ServerProtocol::Https, &None) | ||
| 517 | .unwrap(); | ||
| 518 | assert_eq!(result, "https://github.com/user/repo.git?ref=main"); | ||
| 519 | } | ||
| 520 | |||
| 521 | #[test] | ||
| 522 | fn port_with_parameters_and_fragment() { | ||
| 523 | let result = "github.com:2222/repo.git?version=1.0#section1" | ||
| 524 | .parse::<CloneUrl>() | ||
| 525 | .unwrap() | ||
| 526 | .format_as(&ServerProtocol::Https, &None) | ||
| 527 | .unwrap(); | ||
| 528 | assert_eq!( | ||
| 529 | result, | ||
| 530 | "https://github.com:2222/repo.git?version=1.0#section1" | ||
| 531 | ); | ||
| 532 | } | ||
| 533 | } | ||
| 534 | |||
| 535 | mod with_https_and_additional_features { | ||
| 536 | use super::*; | ||
| 537 | |||
| 538 | #[test] | ||
| 539 | fn credentials_and_they_are_stripped() { | ||
| 540 | let result = "https://username:password@github.com/user/repo.git" | ||
| 541 | .parse::<CloneUrl>() | ||
| 542 | .unwrap() | ||
| 543 | .format_as(&ServerProtocol::Https, &None) | ||
| 544 | .unwrap(); | ||
| 545 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 546 | } | ||
| 547 | |||
| 548 | #[test] | ||
| 549 | fn port() { | ||
| 550 | let result = "https://github.com:1000/user/repo.git" | ||
| 551 | .parse::<CloneUrl>() | ||
| 552 | .unwrap() | ||
| 553 | .format_as(&ServerProtocol::Https, &None) | ||
| 554 | .unwrap(); | ||
| 555 | assert_eq!(result, "https://github.com:1000/user/repo.git"); | ||
| 556 | } | ||
| 557 | |||
| 558 | #[test] | ||
| 559 | fn path_with_fragment() { | ||
| 560 | let result = "https://github.com/user/repo.git#readme" | ||
| 561 | .parse::<CloneUrl>() | ||
| 562 | .unwrap() | ||
| 563 | .format_as(&ServerProtocol::Https, &None) | ||
| 564 | .unwrap(); | ||
| 565 | assert_eq!(result, "https://github.com/user/repo.git#readme"); | ||
| 566 | } | ||
| 567 | |||
| 568 | #[test] | ||
| 569 | fn path_with_parameters() { | ||
| 570 | let result = "https://github.com/user/repo.git?ref=main" | ||
| 571 | .parse::<CloneUrl>() | ||
| 572 | .unwrap() | ||
| 573 | .format_as(&ServerProtocol::Https, &None) | ||
| 574 | .unwrap(); | ||
| 575 | assert_eq!(result, "https://github.com/user/repo.git?ref=main"); | ||
| 576 | } | ||
| 577 | |||
| 578 | #[test] | ||
| 579 | fn port_with_parameters_and_fragment() { | ||
| 580 | let result = "https://github.com:2222/repo.git?version=1.0#section1" | ||
| 581 | .parse::<CloneUrl>() | ||
| 582 | .unwrap() | ||
| 583 | .format_as(&ServerProtocol::Https, &None) | ||
| 584 | .unwrap(); | ||
| 585 | assert_eq!( | ||
| 586 | result, | ||
| 587 | "https://github.com:2222/repo.git?version=1.0#section1" | ||
| 588 | ); | ||
| 589 | } | ||
| 590 | } | ||
| 591 | |||
| 592 | #[test] | ||
| 593 | fn test_http_url() { | ||
| 594 | let result = "http://github.com/user/repo.git" | ||
| 595 | .parse::<CloneUrl>() | ||
| 596 | .unwrap() | ||
| 597 | .format_as(&ServerProtocol::Https, &None) | ||
| 598 | .unwrap(); | ||
| 599 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 600 | } | ||
| 601 | |||
| 602 | mod ssh_input { | ||
| 603 | use super::*; | ||
| 604 | |||
| 605 | #[test] | ||
| 606 | fn test_git_at_url() { | ||
| 607 | let result = "git@github.com:user/repo.git" | ||
| 608 | .parse::<CloneUrl>() | ||
| 609 | .unwrap() | ||
| 610 | .format_as(&ServerProtocol::Https, &None) | ||
| 611 | .unwrap(); | ||
| 612 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 613 | } | ||
| 614 | |||
| 615 | #[test] | ||
| 616 | fn test_user_at_url() { | ||
| 617 | let result = "user1@github.com:user/repo.git" | ||
| 618 | .parse::<CloneUrl>() | ||
| 619 | .unwrap() | ||
| 620 | .format_as(&ServerProtocol::Https, &None) | ||
| 621 | .unwrap(); | ||
| 622 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 623 | } | ||
| 624 | #[test] | ||
| 625 | fn path_has_colon_slash_prefix() { | ||
| 626 | let result = "user1@github.com:/user/repo.git" | ||
| 627 | .parse::<CloneUrl>() | ||
| 628 | .unwrap() | ||
| 629 | .format_as(&ServerProtocol::Https, &None) | ||
| 630 | .unwrap(); | ||
| 631 | assert_eq!(result, "https://github.com/user/repo.git"); | ||
| 632 | } | ||
| 633 | |||
| 634 | #[test] | ||
| 635 | fn port_specified_with_path() { | ||
| 636 | let result = "user@github.com:2222/repo.git" | ||
| 637 | .parse::<CloneUrl>() | ||
| 638 | .unwrap() | ||
| 639 | .format_as(&ServerProtocol::Https, &None) | ||
| 640 | .unwrap(); | ||
| 641 | assert_eq!(result, "https://github.com:2222/repo.git"); | ||
| 642 | } | ||
| 643 | |||
| 644 | #[test] | ||
| 645 | fn port_specified_without_path() { | ||
| 646 | let result = "user@github.com:2222" | ||
| 647 | .parse::<CloneUrl>() | ||
| 648 | .unwrap() | ||
| 649 | .format_as(&ServerProtocol::Https, &None) | ||
| 650 | .unwrap(); | ||
| 651 | assert_eq!(result, "https://github.com:2222"); | ||
| 652 | } | ||
| 653 | |||
| 654 | #[test] | ||
| 655 | fn path_with_fragment() { | ||
| 656 | let result = "user1@github.com:/user/repo.git#readme" | ||
| 657 | .parse::<CloneUrl>() | ||
| 658 | .unwrap() | ||
| 659 | .format_as(&ServerProtocol::Https, &None) | ||
| 660 | .unwrap(); | ||
| 661 | assert_eq!(result, "https://github.com/user/repo.git#readme"); | ||
| 662 | } | ||
| 663 | |||
| 664 | #[test] | ||
| 665 | fn path_with_parameters() { | ||
| 666 | let result = "user@github.com:/user/repo.git?ref=main" | ||
| 667 | .parse::<CloneUrl>() | ||
| 668 | .unwrap() | ||
| 669 | .format_as(&ServerProtocol::Https, &None) | ||
| 670 | .unwrap(); | ||
| 671 | assert_eq!(result, "https://github.com/user/repo.git?ref=main"); | ||
| 672 | } | ||
| 673 | |||
| 674 | #[test] | ||
| 675 | fn port_with_parameters_and_fragment() { | ||
| 676 | let result = "user@github.com:2222/repo.git?version=1.0#section1" | ||
| 677 | .parse::<CloneUrl>() | ||
| 678 | .unwrap() | ||
| 679 | .format_as(&ServerProtocol::Https, &None) | ||
| 680 | .unwrap(); | ||
| 681 | assert_eq!( | ||
| 682 | result, | ||
| 683 | "https://github.com:2222/repo.git?version=1.0#section1" | ||
| 684 | ); | ||
| 685 | } | ||
| 686 | } | ||
| 687 | |||
| 688 | #[test] | ||
| 689 | fn test_ftp_url() { | ||
| 690 | let result = "ftp://example.com/repo.git" | ||
| 691 | .parse::<CloneUrl>() | ||
| 692 | .unwrap() | ||
| 693 | .format_as(&ServerProtocol::Https, &None) | ||
| 694 | .unwrap(); | ||
| 695 | assert_eq!(result, "https://example.com/repo.git"); | ||
| 696 | } | ||
| 697 | |||
| 698 | #[test] | ||
| 699 | fn test_git_protocol_url() { | ||
| 700 | let result = "git://example.com/repo.git" | ||
| 701 | .parse::<CloneUrl>() | ||
| 702 | .unwrap() | ||
| 703 | .format_as(&ServerProtocol::Https, &None) | ||
| 704 | .unwrap(); | ||
| 705 | assert_eq!(result, "https://example.com/repo.git"); | ||
| 706 | } | ||
| 707 | |||
| 708 | #[test] | ||
| 709 | fn test_invalid_url() { | ||
| 710 | let clone_url_result = "unsupported://example.com/repo.git".parse::<CloneUrl>(); | ||
| 711 | assert!(clone_url_result.is_err()); | ||
| 712 | } | ||
| 713 | mod local_addresses_should_return_error { | ||
| 714 | use super::*; | ||
| 715 | #[test] | ||
| 716 | fn test_absolute_local_path() { | ||
| 717 | let result = "/path/to/repo.git" | ||
| 718 | .parse::<CloneUrl>() | ||
| 719 | .unwrap() | ||
| 720 | .format_as(&ServerProtocol::Https, &None); | ||
| 721 | assert!(result.is_err()); // Expecting an error when converting to HTTPS | ||
| 722 | } | ||
| 723 | |||
| 724 | #[test] | ||
| 725 | fn test_relative_local_path() { | ||
| 726 | let result = "./path/to/repo.git" | ||
| 727 | .parse::<CloneUrl>() | ||
| 728 | .unwrap() | ||
| 729 | .format_as(&ServerProtocol::Https, &None); | ||
| 730 | assert!(result.is_err()); // Expecting an error when converting to HTTPS | ||
| 731 | } | ||
| 732 | } | ||
| 733 | } | ||
| 734 | } | ||
| 194 | mod convert_clone_url_to_https { | 735 | mod convert_clone_url_to_https { |
| 195 | use super::*; | 736 | use super::*; |
| 196 | 737 | ||