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.rs289
1 files changed, 146 insertions, 143 deletions
diff --git a/src/lib/git/nostr_url.rs b/src/lib/git/nostr_url.rs
index 5e92a84..0a8338a 100644
--- a/src/lib/git/nostr_url.rs
+++ b/src/lib/git/nostr_url.rs
@@ -2,6 +2,7 @@ use core::fmt;
2use std::{collections::HashMap, str::FromStr}; 2use std::{collections::HashMap, str::FromStr};
3 3
4use anyhow::{Context, Error, Result, anyhow, bail}; 4use anyhow::{Context, Error, Result, anyhow, bail};
5use directories::BaseDirs;
5use nostr::nips::{nip01::Coordinate, nip19::Nip19Coordinate}; 6use nostr::nips::{nip01::Coordinate, nip19::Nip19Coordinate};
6use nostr_sdk::{FromBech32, PublicKey, RelayUrl, ToBech32, Url}; 7use nostr_sdk::{FromBech32, PublicKey, RelayUrl, ToBech32, Url};
7 8
@@ -61,7 +62,7 @@ pub struct NostrUrlDecoded {
61 pub original_string: String, 62 pub original_string: String,
62 pub coordinate: Nip19Coordinate, 63 pub coordinate: Nip19Coordinate,
63 pub protocol: Option<ServerProtocol>, 64 pub protocol: Option<ServerProtocol>,
64 pub user: Option<String>, 65 pub ssh_key_file: Option<String>,
65 pub nip05: Option<String>, 66 pub nip05: Option<String>,
66} 67}
67 68
@@ -71,8 +72,8 @@ impl fmt::Display for NostrUrlDecoded {
71 return write!(f, "{}", self.original_string); 72 return write!(f, "{}", self.original_string);
72 } 73 }
73 write!(f, "nostr://")?; 74 write!(f, "nostr://")?;
74 if let Some(user) = &self.user { 75 if let Some(ssh_key_file) = &self.ssh_key_file {
75 write!(f, "{user}@")?; 76 write!(f, "{ssh_key_file}@")?;
76 } 77 }
77 if let Some(protocol) = &self.protocol { 78 if let Some(protocol) = &self.protocol {
78 write!(f, "{protocol}/")?; 79 write!(f, "{protocol}/")?;
@@ -103,7 +104,7 @@ static INCORRECT_NOSTR_URL_FORMAT_ERROR: &str = "incorrect nostr git url format.
103impl NostrUrlDecoded { 104impl NostrUrlDecoded {
104 pub async fn parse_and_resolve(url: &str, git_repo: &Option<&Repo>) -> Result<Self> { 105 pub async fn parse_and_resolve(url: &str, git_repo: &Option<&Repo>) -> Result<Self> {
105 let mut protocol = None; 106 let mut protocol = None;
106 let mut user = None; 107 let mut ssh_key_file = None;
107 let mut relays = vec![]; 108 let mut relays = vec![];
108 let mut nip05 = None; 109 let mut nip05 = None;
109 110
@@ -130,8 +131,8 @@ impl NostrUrlDecoded {
130 "git" => Some(ServerProtocol::Git), 131 "git" => Some(ServerProtocol::Git),
131 _ => None, 132 _ => None,
132 }; 133 };
133 } else if name == "user" { 134 } else if name == "ssh_key_file" {
134 user = Some(value.to_string()); 135 ssh_key_file = Some(value.to_string());
135 } 136 }
136 } 137 }
137 138
@@ -148,7 +149,7 @@ impl NostrUrlDecoded {
148 let protocol_str = if part.contains('.') { 149 let protocol_str = if part.contains('.') {
149 part 150 part
150 } else if let Some(at_index) = part.find('@') { 151 } else if let Some(at_index) = part.find('@') {
151 user = Some(part[..at_index].to_string()); 152 ssh_key_file = Some(part[..at_index].to_string());
152 &part[at_index + 1..] 153 &part[at_index + 1..]
153 } else { 154 } else {
154 part 155 part
@@ -240,10 +241,30 @@ impl NostrUrlDecoded {
240 original_string: url.to_string(), 241 original_string: url.to_string(),
241 coordinate, 242 coordinate,
242 protocol, 243 protocol,
243 user, 244 ssh_key_file,
244 nip05, 245 nip05,
245 }) 246 })
246 } 247 }
248
249 pub fn ssh_key_file_path(&self) -> Option<String> {
250 if let Some(ssh_key_file) = &self.ssh_key_file {
251 if !ssh_key_file.is_empty() {
252 // checking if path exists would make unit tests harder
253 if is_absoute_or_relative_path(ssh_key_file) {
254 return Some(ssh_key_file.clone());
255 } else if let Some(dirs) = BaseDirs::new() {
256 return Some(
257 dirs.home_dir()
258 .join(".ssh")
259 .join(ssh_key_file)
260 .to_string_lossy()
261 .into_owned(),
262 );
263 }
264 }
265 }
266 None
267 }
247} 268}
248 269
249fn resolve_nip05_from_git_config_cache(nip05: &str, git_repo: &Option<&Repo>) -> Result<PublicKey> { 270fn resolve_nip05_from_git_config_cache(nip05: &str, git_repo: &Option<&Repo>) -> Result<PublicKey> {
@@ -297,6 +318,13 @@ fn load_nip_cache(git_repo: &Option<&Repo>) -> Result<HashMap<String, PublicKey>
297 Ok(h) 318 Ok(h)
298} 319}
299 320
321fn is_absoute_or_relative_path(input: &str) -> bool {
322 ["~", "/", "./", "../", ".\\", "..\\"]
323 .iter()
324 .any(|s| input.starts_with(s))
325 || input.chars().nth(1).unwrap() == ':'
326}
327
300#[derive(Debug, PartialEq, Default)] 328#[derive(Debug, PartialEq, Default)]
301pub struct CloneUrl { 329pub struct CloneUrl {
302 original_string: String, 330 original_string: String,
@@ -324,7 +352,7 @@ impl FromStr for CloneUrl {
324 let url_str = if s.contains("://") { 352 let url_str = if s.contains("://") {
325 s.to_string() // Use the original string 353 s.to_string() // Use the original string
326 } else { 354 } else {
327 let protocol = // Check for the SSH format user@host:path and convert to ssh:// 355 let protocol = // Check for the SSH format git@host:path and convert to ssh://
328 if s.contains('@') && s 356 if s.contains('@') && s
329 .split('@') 357 .split('@')
330 .nth(0) 358 .nth(0)
@@ -395,7 +423,7 @@ fn contains_port(s: &str) -> bool {
395} 423}
396 424
397impl CloneUrl { 425impl CloneUrl {
398 pub fn format_as(&self, protocol: &ServerProtocol, user: &Option<String>) -> Result<String> { 426 pub fn format_as(&self, protocol: &ServerProtocol) -> Result<String> {
399 // Check for incompatible protocol conversions 427 // Check for incompatible protocol conversions
400 if *protocol == ServerProtocol::Filesystem { 428 if *protocol == ServerProtocol::Filesystem {
401 if self.protocol == ServerProtocol::Filesystem { 429 if self.protocol == ServerProtocol::Filesystem {
@@ -450,10 +478,7 @@ impl CloneUrl {
450 let mut formatted_url = url.to_string(); 478 let mut formatted_url = url.to_string();
451 479
452 if *protocol == ServerProtocol::Ssh { 480 if *protocol == ServerProtocol::Ssh {
453 formatted_url = formatted_url.replace( 481 formatted_url = formatted_url.replace("ssh://", "git@");
454 "ssh://",
455 format!("{}@", user.as_deref().unwrap_or("git")).as_str(),
456 );
457 if url.port().is_some() { 482 if url.port().is_some() {
458 formatted_url = format!("ssh://{formatted_url}"); 483 formatted_url = format!("ssh://{formatted_url}");
459 } else { 484 } else {
@@ -543,7 +568,7 @@ fn strip_credentials(url: &str) -> String {
543 return format!("{}{}", protocol, rest_parts[1]); 568 return format!("{}{}", protocol, rest_parts[1]);
544 } 569 }
545 } else if let Some(at_pos) = url.find('@') { 570 } else if let Some(at_pos) = url.find('@') {
546 // Handle user@host:path format 571 // Handle git@host:path format
547 let (_, rest) = url.split_at(at_pos); 572 let (_, rest) = url.split_at(at_pos);
548 // This is a git@ syntax 573 // This is a git@ syntax
549 let host_and_repo = &rest[1..]; // Skip the ':' 574 let host_and_repo = &rest[1..]; // Skip the ':'
@@ -565,52 +590,12 @@ mod tests {
565 mod clone_url_from_str_format_as { 590 mod clone_url_from_str_format_as {
566 use super::*; 591 use super::*;
567 592
568 mod when_user_specified {
569 use super::*;
570
571 mod but_not_in_original_url {
572 use super::*;
573
574 #[test]
575 fn https_to_https_ignores_user() {
576 let result = "https://github.com/user/repo.git"
577 .parse::<CloneUrl>()
578 .unwrap()
579 .format_as(&ServerProtocol::Https, &Some("user1".to_string()))
580 .unwrap();
581 assert_eq!(result, "https://github.com/user/repo.git");
582 }
583 #[test]
584 fn https_to_ssh_uses_specified_user() {
585 let result = "https://github.com/user/repo.git"
586 .parse::<CloneUrl>()
587 .unwrap()
588 .format_as(&ServerProtocol::Ssh, &Some("user1".to_string()))
589 .unwrap();
590 assert_eq!(result, "user1@github.com:user/repo.git");
591 }
592 }
593 mod and_a_different_user_in_original_url {
594 use super::*;
595
596 #[test]
597 fn ssh_uses_specified_user() {
598 let result = "user2@github.com/user/repo.git"
599 .parse::<CloneUrl>()
600 .unwrap()
601 .format_as(&ServerProtocol::Ssh, &Some("user1".to_string()))
602 .unwrap();
603 assert_eq!(result, "user1@github.com:user/repo.git");
604 }
605 }
606 }
607
608 #[test] 593 #[test]
609 fn format_as_ssh_defaults_to_git_user() { 594 fn format_as_ssh_defaults_to_git_user() {
610 let result = "https://github.com/user/repo.git" 595 let result = "https://github.com/user/repo.git"
611 .parse::<CloneUrl>() 596 .parse::<CloneUrl>()
612 .unwrap() 597 .unwrap()
613 .format_as(&ServerProtocol::Ssh, &None) 598 .format_as(&ServerProtocol::Ssh)
614 .unwrap(); 599 .unwrap();
615 assert_eq!(result, "git@github.com:user/repo.git"); 600 assert_eq!(result, "git@github.com:user/repo.git");
616 } 601 }
@@ -623,7 +608,7 @@ mod tests {
623 let result = "https://github.com:1000/user/repo.git" 608 let result = "https://github.com:1000/user/repo.git"
624 .parse::<CloneUrl>() 609 .parse::<CloneUrl>()
625 .unwrap() 610 .unwrap()
626 .format_as(&ServerProtocol::Https, &None) 611 .format_as(&ServerProtocol::Https)
627 .unwrap(); 612 .unwrap();
628 assert_eq!(result, "https://github.com:1000/user/repo.git"); 613 assert_eq!(result, "https://github.com:1000/user/repo.git");
629 } 614 }
@@ -633,7 +618,7 @@ mod tests {
633 let result = "https://github.com:1000/user/repo.git" 618 let result = "https://github.com:1000/user/repo.git"
634 .parse::<CloneUrl>() 619 .parse::<CloneUrl>()
635 .unwrap() 620 .unwrap()
636 .format_as(&ServerProtocol::Ssh, &None) 621 .format_as(&ServerProtocol::Ssh)
637 .unwrap(); 622 .unwrap();
638 assert_eq!(result, "git@github.com:user/repo.git"); 623 assert_eq!(result, "git@github.com:user/repo.git");
639 } 624 }
@@ -644,7 +629,7 @@ mod tests {
644 let result = "ssh://git@github.com:29418/user/repo.git" 629 let result = "ssh://git@github.com:29418/user/repo.git"
645 .parse::<CloneUrl>() 630 .parse::<CloneUrl>()
646 .unwrap() 631 .unwrap()
647 .format_as(&ServerProtocol::Ssh, &None) 632 .format_as(&ServerProtocol::Ssh)
648 .unwrap(); 633 .unwrap();
649 // need this format 634 // need this format
650 assert_eq!(result, "ssh://git@github.com:29418/user/repo.git"); 635 assert_eq!(result, "ssh://git@github.com:29418/user/repo.git");
@@ -655,7 +640,7 @@ mod tests {
655 let result = "ssh://git@github.com:29418/user/repo.git" 640 let result = "ssh://git@github.com:29418/user/repo.git"
656 .parse::<CloneUrl>() 641 .parse::<CloneUrl>()
657 .unwrap() 642 .unwrap()
658 .format_as(&ServerProtocol::Https, &None) 643 .format_as(&ServerProtocol::Https)
659 .unwrap(); 644 .unwrap();
660 // need this format 645 // need this format
661 assert_eq!(result, "https://github.com/user/repo.git"); 646 assert_eq!(result, "https://github.com/user/repo.git");
@@ -667,7 +652,7 @@ mod tests {
667 let result = "git@github.com:29418/user/repo.git" 652 let result = "git@github.com:29418/user/repo.git"
668 .parse::<CloneUrl>() 653 .parse::<CloneUrl>()
669 .unwrap() 654 .unwrap()
670 .format_as(&ServerProtocol::Ssh, &None) 655 .format_as(&ServerProtocol::Ssh)
671 .unwrap(); 656 .unwrap();
672 // need this format 657 // need this format
673 assert_eq!(result, "ssh://git@github.com:29418/user/repo.git"); 658 assert_eq!(result, "ssh://git@github.com:29418/user/repo.git");
@@ -679,7 +664,7 @@ mod tests {
679 let result = "https://github.com/user/repo.git" 664 let result = "https://github.com/user/repo.git"
680 .parse::<CloneUrl>() 665 .parse::<CloneUrl>()
681 .unwrap() 666 .unwrap()
682 .format_as(&ServerProtocol::Unspecified, &None) 667 .format_as(&ServerProtocol::Unspecified)
683 .unwrap(); 668 .unwrap();
684 assert_eq!(result, "github.com/user/repo.git"); 669 assert_eq!(result, "github.com/user/repo.git");
685 } 670 }
@@ -692,7 +677,7 @@ mod tests {
692 let result = "https://github.com/user/repo.git" 677 let result = "https://github.com/user/repo.git"
693 .parse::<CloneUrl>() 678 .parse::<CloneUrl>()
694 .unwrap() 679 .unwrap()
695 .format_as(&ServerProtocol::Https, &None) 680 .format_as(&ServerProtocol::Https)
696 .unwrap(); 681 .unwrap();
697 assert_eq!(result, "https://github.com/user/repo.git"); 682 assert_eq!(result, "https://github.com/user/repo.git");
698 } 683 }
@@ -705,7 +690,7 @@ mod tests {
705 let result = "github.com:1000/user/repo.git" 690 let result = "github.com:1000/user/repo.git"
706 .parse::<CloneUrl>() 691 .parse::<CloneUrl>()
707 .unwrap() 692 .unwrap()
708 .format_as(&ServerProtocol::Https, &None) 693 .format_as(&ServerProtocol::Https)
709 .unwrap(); 694 .unwrap();
710 assert_eq!(result, "https://github.com/user/repo.git"); 695 assert_eq!(result, "https://github.com/user/repo.git");
711 } 696 }
@@ -715,7 +700,7 @@ mod tests {
715 let result = "github.com:user/repo.git" 700 let result = "github.com:user/repo.git"
716 .parse::<CloneUrl>() 701 .parse::<CloneUrl>()
717 .unwrap() 702 .unwrap()
718 .format_as(&ServerProtocol::Https, &None) 703 .format_as(&ServerProtocol::Https)
719 .unwrap(); 704 .unwrap();
720 assert_eq!(result, "https://github.com/user/repo.git"); 705 assert_eq!(result, "https://github.com/user/repo.git");
721 } 706 }
@@ -725,7 +710,7 @@ mod tests {
725 let result = "github.com/user/repo.git#readme" 710 let result = "github.com/user/repo.git#readme"
726 .parse::<CloneUrl>() 711 .parse::<CloneUrl>()
727 .unwrap() 712 .unwrap()
728 .format_as(&ServerProtocol::Https, &None) 713 .format_as(&ServerProtocol::Https)
729 .unwrap(); 714 .unwrap();
730 assert_eq!(result, "https://github.com/user/repo.git#readme"); 715 assert_eq!(result, "https://github.com/user/repo.git#readme");
731 } 716 }
@@ -735,7 +720,7 @@ mod tests {
735 let result = "github.com/user/repo.git?ref=main" 720 let result = "github.com/user/repo.git?ref=main"
736 .parse::<CloneUrl>() 721 .parse::<CloneUrl>()
737 .unwrap() 722 .unwrap()
738 .format_as(&ServerProtocol::Https, &None) 723 .format_as(&ServerProtocol::Https)
739 .unwrap(); 724 .unwrap();
740 assert_eq!(result, "https://github.com/user/repo.git?ref=main"); 725 assert_eq!(result, "https://github.com/user/repo.git?ref=main");
741 } 726 }
@@ -745,7 +730,7 @@ mod tests {
745 let result = "github.com:2222/repo.git?version=1.0#section1" 730 let result = "github.com:2222/repo.git?version=1.0#section1"
746 .parse::<CloneUrl>() 731 .parse::<CloneUrl>()
747 .unwrap() 732 .unwrap()
748 .format_as(&ServerProtocol::Https, &None) 733 .format_as(&ServerProtocol::Https)
749 .unwrap(); 734 .unwrap();
750 assert_eq!(result, "https://github.com/repo.git?version=1.0#section1"); 735 assert_eq!(result, "https://github.com/repo.git?version=1.0#section1");
751 } 736 }
@@ -759,7 +744,7 @@ mod tests {
759 let result = "https://username:password@github.com/user/repo.git" 744 let result = "https://username:password@github.com/user/repo.git"
760 .parse::<CloneUrl>() 745 .parse::<CloneUrl>()
761 .unwrap() 746 .unwrap()
762 .format_as(&ServerProtocol::Https, &None) 747 .format_as(&ServerProtocol::Https)
763 .unwrap(); 748 .unwrap();
764 assert_eq!(result, "https://github.com/user/repo.git"); 749 assert_eq!(result, "https://github.com/user/repo.git");
765 } 750 }
@@ -769,7 +754,7 @@ mod tests {
769 let result = "https://github.com:1000/user/repo.git" 754 let result = "https://github.com:1000/user/repo.git"
770 .parse::<CloneUrl>() 755 .parse::<CloneUrl>()
771 .unwrap() 756 .unwrap()
772 .format_as(&ServerProtocol::Https, &None) 757 .format_as(&ServerProtocol::Https)
773 .unwrap(); 758 .unwrap();
774 assert_eq!(result, "https://github.com:1000/user/repo.git"); 759 assert_eq!(result, "https://github.com:1000/user/repo.git");
775 } 760 }
@@ -779,7 +764,7 @@ mod tests {
779 let result = "https://github.com/user/repo.git#readme" 764 let result = "https://github.com/user/repo.git#readme"
780 .parse::<CloneUrl>() 765 .parse::<CloneUrl>()
781 .unwrap() 766 .unwrap()
782 .format_as(&ServerProtocol::Https, &None) 767 .format_as(&ServerProtocol::Https)
783 .unwrap(); 768 .unwrap();
784 assert_eq!(result, "https://github.com/user/repo.git#readme"); 769 assert_eq!(result, "https://github.com/user/repo.git#readme");
785 } 770 }
@@ -789,7 +774,7 @@ mod tests {
789 let result = "https://github.com/user/repo.git?ref=main" 774 let result = "https://github.com/user/repo.git?ref=main"
790 .parse::<CloneUrl>() 775 .parse::<CloneUrl>()
791 .unwrap() 776 .unwrap()
792 .format_as(&ServerProtocol::Https, &None) 777 .format_as(&ServerProtocol::Https)
793 .unwrap(); 778 .unwrap();
794 assert_eq!(result, "https://github.com/user/repo.git?ref=main"); 779 assert_eq!(result, "https://github.com/user/repo.git?ref=main");
795 } 780 }
@@ -799,7 +784,7 @@ mod tests {
799 let result = "https://github.com:2222/repo.git?version=1.0#section1" 784 let result = "https://github.com:2222/repo.git?version=1.0#section1"
800 .parse::<CloneUrl>() 785 .parse::<CloneUrl>()
801 .unwrap() 786 .unwrap()
802 .format_as(&ServerProtocol::Https, &None) 787 .format_as(&ServerProtocol::Https)
803 .unwrap(); 788 .unwrap();
804 assert_eq!( 789 assert_eq!(
805 result, 790 result,
@@ -813,7 +798,7 @@ mod tests {
813 let result = "http://github.com/user/repo.git" 798 let result = "http://github.com/user/repo.git"
814 .parse::<CloneUrl>() 799 .parse::<CloneUrl>()
815 .unwrap() 800 .unwrap()
816 .format_as(&ServerProtocol::Https, &None) 801 .format_as(&ServerProtocol::Https)
817 .unwrap(); 802 .unwrap();
818 assert_eq!(result, "https://github.com/user/repo.git"); 803 assert_eq!(result, "https://github.com/user/repo.git");
819 } 804 }
@@ -826,56 +811,47 @@ mod tests {
826 let result = "git@github.com:user/repo.git" 811 let result = "git@github.com:user/repo.git"
827 .parse::<CloneUrl>() 812 .parse::<CloneUrl>()
828 .unwrap() 813 .unwrap()
829 .format_as(&ServerProtocol::Https, &None) 814 .format_as(&ServerProtocol::Https)
830 .unwrap(); 815 .unwrap();
831 assert_eq!(result, "https://github.com/user/repo.git"); 816 assert_eq!(result, "https://github.com/user/repo.git");
832 } 817 }
833 818
834 #[test] 819 #[test]
835 fn test_user_at_url() {
836 let result = "user1@github.com:user/repo.git"
837 .parse::<CloneUrl>()
838 .unwrap()
839 .format_as(&ServerProtocol::Https, &None)
840 .unwrap();
841 assert_eq!(result, "https://github.com/user/repo.git");
842 }
843 #[test]
844 fn path_has_colon_slash_prefix() { 820 fn path_has_colon_slash_prefix() {
845 let result = "user1@github.com:/user/repo.git" 821 let result = "git@github.com:/user/repo.git"
846 .parse::<CloneUrl>() 822 .parse::<CloneUrl>()
847 .unwrap() 823 .unwrap()
848 .format_as(&ServerProtocol::Https, &None) 824 .format_as(&ServerProtocol::Https)
849 .unwrap(); 825 .unwrap();
850 assert_eq!(result, "https://github.com/user/repo.git"); 826 assert_eq!(result, "https://github.com/user/repo.git");
851 } 827 }
852 828
853 #[test] 829 #[test]
854 fn path_with_fragment() { 830 fn path_with_fragment() {
855 let result = "user1@github.com:/user/repo.git#readme" 831 let result = "git@github.com:/user/repo.git#readme"
856 .parse::<CloneUrl>() 832 .parse::<CloneUrl>()
857 .unwrap() 833 .unwrap()
858 .format_as(&ServerProtocol::Https, &None) 834 .format_as(&ServerProtocol::Https)
859 .unwrap(); 835 .unwrap();
860 assert_eq!(result, "https://github.com/user/repo.git#readme"); 836 assert_eq!(result, "https://github.com/user/repo.git#readme");
861 } 837 }
862 838
863 #[test] 839 #[test]
864 fn path_with_parameters() { 840 fn path_with_parameters() {
865 let result = "user@github.com:/user/repo.git?ref=main" 841 let result = "git@github.com:/user/repo.git?ref=main"
866 .parse::<CloneUrl>() 842 .parse::<CloneUrl>()
867 .unwrap() 843 .unwrap()
868 .format_as(&ServerProtocol::Https, &None) 844 .format_as(&ServerProtocol::Https)
869 .unwrap(); 845 .unwrap();
870 assert_eq!(result, "https://github.com/user/repo.git?ref=main"); 846 assert_eq!(result, "https://github.com/user/repo.git?ref=main");
871 } 847 }
872 848
873 #[test] 849 #[test]
874 fn port_with_parameters_and_fragment_ssh() { 850 fn port_with_parameters_and_fragment_ssh() {
875 let result = "user@github.com:2222/repo.git?version=1.0#section1" 851 let result = "git@github.com:2222/repo.git?version=1.0#section1"
876 .parse::<CloneUrl>() 852 .parse::<CloneUrl>()
877 .unwrap() 853 .unwrap()
878 .format_as(&ServerProtocol::Ssh, &None) 854 .format_as(&ServerProtocol::Ssh)
879 .unwrap(); 855 .unwrap();
880 assert_eq!( 856 assert_eq!(
881 result, 857 result,
@@ -884,10 +860,10 @@ mod tests {
884 } 860 }
885 #[test] 861 #[test]
886 fn port_with_parameters_and_fragment_https() { 862 fn port_with_parameters_and_fragment_https() {
887 let result = "user@github.com:2222/repo.git?version=1.0#section1" 863 let result = "git@github.com:2222/repo.git?version=1.0#section1"
888 .parse::<CloneUrl>() 864 .parse::<CloneUrl>()
889 .unwrap() 865 .unwrap()
890 .format_as(&ServerProtocol::Https, &None) 866 .format_as(&ServerProtocol::Https)
891 .unwrap(); 867 .unwrap();
892 assert_eq!(result, "https://github.com/repo.git?version=1.0#section1"); 868 assert_eq!(result, "https://github.com/repo.git?version=1.0#section1");
893 } 869 }
@@ -898,7 +874,7 @@ mod tests {
898 let result = "ftp://example.com/repo.git" 874 let result = "ftp://example.com/repo.git"
899 .parse::<CloneUrl>() 875 .parse::<CloneUrl>()
900 .unwrap() 876 .unwrap()
901 .format_as(&ServerProtocol::Https, &None) 877 .format_as(&ServerProtocol::Https)
902 .unwrap(); 878 .unwrap();
903 assert_eq!(result, "https://example.com/repo.git"); 879 assert_eq!(result, "https://example.com/repo.git");
904 } 880 }
@@ -908,7 +884,7 @@ mod tests {
908 let result = "git://example.com/repo.git" 884 let result = "git://example.com/repo.git"
909 .parse::<CloneUrl>() 885 .parse::<CloneUrl>()
910 .unwrap() 886 .unwrap()
911 .format_as(&ServerProtocol::Https, &None) 887 .format_as(&ServerProtocol::Https)
912 .unwrap(); 888 .unwrap();
913 assert_eq!(result, "https://example.com/repo.git"); 889 assert_eq!(result, "https://example.com/repo.git");
914 } 890 }
@@ -925,7 +901,7 @@ mod tests {
925 let result = "/path/to/repo.git" 901 let result = "/path/to/repo.git"
926 .parse::<CloneUrl>() 902 .parse::<CloneUrl>()
927 .unwrap() 903 .unwrap()
928 .format_as(&ServerProtocol::Https, &None); 904 .format_as(&ServerProtocol::Https);
929 assert!(result.is_err()); // Expecting an error when converting to HTTPS 905 assert!(result.is_err()); // Expecting an error when converting to HTTPS
930 } 906 }
931 907
@@ -934,7 +910,7 @@ mod tests {
934 let result = "./path/to/repo.git" 910 let result = "./path/to/repo.git"
935 .parse::<CloneUrl>() 911 .parse::<CloneUrl>()
936 .unwrap() 912 .unwrap()
937 .format_as(&ServerProtocol::Https, &None); 913 .format_as(&ServerProtocol::Https);
938 assert!(result.is_err()); // Expecting an error when converting to HTTPS 914 assert!(result.is_err()); // Expecting an error when converting to HTTPS
939 } 915 }
940 } 916 }
@@ -972,13 +948,6 @@ mod tests {
972 } 948 }
973 949
974 #[test] 950 #[test]
975 fn test_user_at_url() {
976 let url = "user1@github.com:user/repo.git";
977 let result = convert_clone_url_to_https(url).unwrap();
978 assert_eq!(result, "https://github.com/user/repo.git");
979 }
980
981 #[test]
982 fn test_ssh_url() { 951 fn test_ssh_url() {
983 let url = "ssh://github.com/user/repo.git"; 952 let url = "ssh://github.com/user/repo.git";
984 let result = convert_clone_url_to_https(url).unwrap(); 953 let result = convert_clone_url_to_https(url).unwrap();
@@ -1030,7 +999,7 @@ mod tests {
1030 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()], 999 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()],
1031 }, 1000 },
1032 protocol: None, 1001 protocol: None,
1033 user: None, 1002 ssh_key_file: None,
1034 nip05: None, 1003 nip05: None,
1035 }), 1004 }),
1036 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit", 1005 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit",
@@ -1055,7 +1024,7 @@ mod tests {
1055 relays: vec![], 1024 relays: vec![],
1056 }, 1025 },
1057 protocol: None, 1026 protocol: None,
1058 user: None, 1027 ssh_key_file: None,
1059 nip05: None, 1028 nip05: None,
1060 }), 1029 }),
1061 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit", 1030 "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit",
@@ -1080,7 +1049,7 @@ mod tests {
1080 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()], 1049 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()],
1081 }, 1050 },
1082 protocol: Some(ServerProtocol::Ssh), 1051 protocol: Some(ServerProtocol::Ssh),
1083 user: None, 1052 ssh_key_file: None,
1084 nip05: None, 1053 nip05: None,
1085 }), 1054 }),
1086 "nostr://ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit", 1055 "nostr://ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit",
@@ -1089,7 +1058,7 @@ mod tests {
1089 } 1058 }
1090 1059
1091 #[test] 1060 #[test]
1092 fn with_protocol_and_user() -> Result<()> { 1061 fn with_protocol_and_ssh_key_file() -> Result<()> {
1093 assert_eq!( 1062 assert_eq!(
1094 format!("{}", NostrUrlDecoded { 1063 format!("{}", NostrUrlDecoded {
1095 original_string: String::new(), 1064 original_string: String::new(),
@@ -1105,7 +1074,7 @@ mod tests {
1105 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()], 1074 relays: vec![RelayUrl::parse("wss://nos.lol").unwrap()],
1106 }, 1075 },
1107 protocol: Some(ServerProtocol::Ssh), 1076 protocol: Some(ServerProtocol::Ssh),
1108 user: Some("bla".to_string()), 1077 ssh_key_file: Some("bla".to_string()),
1109 nip05: None, 1078 nip05: None,
1110 }), 1079 }),
1111 "nostr://bla@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit", 1080 "nostr://bla@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/nos.lol/ngit",
@@ -1114,27 +1083,27 @@ mod tests {
1114 } 1083 }
1115 } 1084 }
1116 1085
1086 fn get_model_coordinate(relays: bool) -> Nip19Coordinate {
1087 Nip19Coordinate {
1088 coordinate: Coordinate {
1089 identifier: "ngit".to_string(),
1090 public_key: PublicKey::parse(
1091 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
1092 )
1093 .unwrap(),
1094 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1095 },
1096 relays: if relays {
1097 vec![RelayUrl::parse("wss://nos.lol").unwrap()]
1098 } else {
1099 vec![]
1100 },
1101 }
1102 }
1103
1117 mod nostr_url_decoded_paramemters_from_str { 1104 mod nostr_url_decoded_paramemters_from_str {
1118 use super::*; 1105 use super::*;
1119 1106
1120 fn get_model_coordinate(relays: bool) -> Nip19Coordinate {
1121 Nip19Coordinate {
1122 coordinate: Coordinate {
1123 identifier: "ngit".to_string(),
1124 public_key: PublicKey::parse(
1125 "npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr",
1126 )
1127 .unwrap(),
1128 kind: nostr_sdk::Kind::GitRepoAnnouncement,
1129 },
1130 relays: if relays {
1131 vec![RelayUrl::parse("wss://nos.lol").unwrap()]
1132 } else {
1133 vec![]
1134 },
1135 }
1136 }
1137
1138 #[tokio::test] 1107 #[tokio::test]
1139 async fn from_naddr() -> Result<()> { 1108 async fn from_naddr() -> Result<()> {
1140 let url = "nostr://naddr1qqzxuemfwsqs6amnwvaz7tmwdaejumr0dspzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqvzqqqrhnym0k2qj".to_string(); 1109 let url = "nostr://naddr1qqzxuemfwsqs6amnwvaz7tmwdaejumr0dspzpgqgmmc409hm4xsdd74sf68a2uyf9pwel4g9mfdg8l5244t6x4jdqvzqqqrhnym0k2qj".to_string();
@@ -1155,7 +1124,7 @@ mod tests {
1155 * slash */ 1124 * slash */
1156 }, 1125 },
1157 protocol: None, 1126 protocol: None,
1158 user: None, 1127 ssh_key_file: None,
1159 nip05: None, 1128 nip05: None,
1160 }, 1129 },
1161 ); 1130 );
@@ -1176,7 +1145,7 @@ mod tests {
1176 original_string: url.clone(), 1145 original_string: url.clone(),
1177 coordinate: get_model_coordinate(false), 1146 coordinate: get_model_coordinate(false),
1178 protocol: None, 1147 protocol: None,
1179 user: None, 1148 ssh_key_file: None,
1180 nip05: None, 1149 nip05: None,
1181 }, 1150 },
1182 ); 1151 );
@@ -1195,7 +1164,7 @@ mod tests {
1195 original_string: url.clone(), 1164 original_string: url.clone(),
1196 coordinate: get_model_coordinate(true), 1165 coordinate: get_model_coordinate(true),
1197 protocol: None, 1166 protocol: None,
1198 user: None, 1167 ssh_key_file: None,
1199 nip05: None, 1168 nip05: None,
1200 }, 1169 },
1201 ); 1170 );
@@ -1214,7 +1183,7 @@ mod tests {
1214 original_string: url.clone(), 1183 original_string: url.clone(),
1215 coordinate: get_model_coordinate(true), 1184 coordinate: get_model_coordinate(true),
1216 protocol: None, 1185 protocol: None,
1217 user: None, 1186 ssh_key_file: None,
1218 nip05: None, 1187 nip05: None,
1219 }, 1188 },
1220 ); 1189 );
@@ -1247,7 +1216,7 @@ mod tests {
1247 ], 1216 ],
1248 }, 1217 },
1249 protocol: None, 1218 protocol: None,
1250 user: None, 1219 ssh_key_file: None,
1251 nip05: None, 1220 nip05: None,
1252 }, 1221 },
1253 ); 1222 );
@@ -1263,7 +1232,7 @@ mod tests {
1263 original_string: url.clone(), 1232 original_string: url.clone(),
1264 coordinate: get_model_coordinate(false), 1233 coordinate: get_model_coordinate(false),
1265 protocol: Some(ServerProtocol::Ssh), 1234 protocol: Some(ServerProtocol::Ssh),
1266 user: None, 1235 ssh_key_file: None,
1267 nip05: None, 1236 nip05: None,
1268 }, 1237 },
1269 ); 1238 );
@@ -1271,15 +1240,15 @@ mod tests {
1271 } 1240 }
1272 1241
1273 #[tokio::test] 1242 #[tokio::test]
1274 async fn with_server_protocol_and_user() -> Result<()> { 1243 async fn with_server_protocol_and_ssh_key_file() -> Result<()> {
1275 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh&user=fred".to_string(); 1244 let url = "nostr://npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit?protocol=ssh&ssh_key_file=fred".to_string();
1276 assert_eq!( 1245 assert_eq!(
1277 NostrUrlDecoded::parse_and_resolve(&url, &None).await?, 1246 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
1278 NostrUrlDecoded { 1247 NostrUrlDecoded {
1279 original_string: url.clone(), 1248 original_string: url.clone(),
1280 coordinate: get_model_coordinate(false), 1249 coordinate: get_model_coordinate(false),
1281 protocol: Some(ServerProtocol::Ssh), 1250 protocol: Some(ServerProtocol::Ssh),
1282 user: Some("fred".to_string()), 1251 ssh_key_file: Some("fred".to_string()),
1283 nip05: None, 1252 nip05: None,
1284 }, 1253 },
1285 ); 1254 );
@@ -1299,7 +1268,7 @@ mod tests {
1299 original_string: url.clone(), 1268 original_string: url.clone(),
1300 coordinate: get_model_coordinate(true), 1269 coordinate: get_model_coordinate(true),
1301 protocol: None, 1270 protocol: None,
1302 user: None, 1271 ssh_key_file: None,
1303 nip05: None, 1272 nip05: None,
1304 }, 1273 },
1305 ); 1274 );
@@ -1318,7 +1287,7 @@ mod tests {
1318 original_string: url.clone(), 1287 original_string: url.clone(),
1319 coordinate: get_model_coordinate(true), 1288 coordinate: get_model_coordinate(true),
1320 protocol: None, 1289 protocol: None,
1321 user: None, 1290 ssh_key_file: None,
1322 nip05: None, 1291 nip05: None,
1323 }, 1292 },
1324 ); 1293 );
@@ -1351,7 +1320,7 @@ mod tests {
1351 ], 1320 ],
1352 }, 1321 },
1353 protocol: None, 1322 protocol: None,
1354 user: None, 1323 ssh_key_file: None,
1355 nip05: None, 1324 nip05: None,
1356 }, 1325 },
1357 ); 1326 );
@@ -1367,7 +1336,7 @@ mod tests {
1367 original_string: url.clone(), 1336 original_string: url.clone(),
1368 coordinate: get_model_coordinate(false), 1337 coordinate: get_model_coordinate(false),
1369 protocol: Some(ServerProtocol::Ssh), 1338 protocol: Some(ServerProtocol::Ssh),
1370 user: None, 1339 ssh_key_file: None,
1371 nip05: None, 1340 nip05: None,
1372 }, 1341 },
1373 ); 1342 );
@@ -1375,7 +1344,7 @@ mod tests {
1375 } 1344 }
1376 1345
1377 #[tokio::test] 1346 #[tokio::test]
1378 async fn with_server_protocol_and_user() -> Result<()> { 1347 async fn with_server_protocol_and_ssh_key_file() -> Result<()> {
1379 let url = "nostr://fred@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit".to_string(); 1348 let url = "nostr://fred@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit".to_string();
1380 assert_eq!( 1349 assert_eq!(
1381 NostrUrlDecoded::parse_and_resolve(&url, &None).await?, 1350 NostrUrlDecoded::parse_and_resolve(&url, &None).await?,
@@ -1383,7 +1352,7 @@ mod tests {
1383 original_string: url.clone(), 1352 original_string: url.clone(),
1384 coordinate: get_model_coordinate(false), 1353 coordinate: get_model_coordinate(false),
1385 protocol: Some(ServerProtocol::Ssh), 1354 protocol: Some(ServerProtocol::Ssh),
1386 user: Some("fred".to_string()), 1355 ssh_key_file: Some("fred".to_string()),
1387 nip05: None, 1356 nip05: None,
1388 }, 1357 },
1389 ); 1358 );
@@ -1392,4 +1361,38 @@ mod tests {
1392 } 1361 }
1393 } 1362 }
1394 } 1363 }
1364 mod nostr_url_ssh_key_file_path {
1365 use super::*;
1366
1367 #[tokio::test]
1368 async fn when_full_file_path_not_detected_default_ssh_dir_is_preppended() -> Result<()> {
1369 let url = "nostr://fred@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit".to_string();
1370 let decoded = NostrUrlDecoded {
1371 original_string: url.clone(),
1372 coordinate: get_model_coordinate(false),
1373 protocol: Some(ServerProtocol::Ssh),
1374 ssh_key_file: Some("fred".to_string()),
1375 nip05: None,
1376 };
1377 assert!(decoded.ssh_key_file_path().unwrap().ends_with("/.ssh/fred"));
1378 Ok(())
1379 }
1380
1381 #[tokio::test]
1382 async fn when_full_file_path_detected_default_ssh_dir_is_not_preppended() -> Result<()> {
1383 let url = "nostr://~/other/fred@ssh/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit".to_string();
1384 let decoded = NostrUrlDecoded {
1385 original_string: url.clone(),
1386 coordinate: get_model_coordinate(false),
1387 protocol: Some(ServerProtocol::Ssh),
1388 ssh_key_file: Some("~/other/fred".to_string()),
1389 nip05: None,
1390 };
1391 assert_eq!(
1392 decoded.ssh_key_file_path(),
1393 Some("~/other/fred".to_string())
1394 );
1395 Ok(())
1396 }
1397 }
1395} 1398}