diff options
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/repo_ref.rs | 216 |
1 files changed, 202 insertions, 14 deletions
diff --git a/src/lib/repo_ref.rs b/src/lib/repo_ref.rs index eb0964f..9573238 100644 --- a/src/lib/repo_ref.rs +++ b/src/lib/repo_ref.rs | |||
| @@ -736,26 +736,56 @@ pub fn extract_npub(s: &str) -> Result<&str> { | |||
| 736 | 736 | ||
| 737 | pub fn is_grasp_server_in_list(url: &str, grasp_servers: &[String]) -> bool { | 737 | pub fn is_grasp_server_in_list(url: &str, grasp_servers: &[String]) -> bool { |
| 738 | if !grasp_servers.is_empty() { | 738 | if !grasp_servers.is_empty() { |
| 739 | if let Ok(url) = normalize_grasp_server_url(url) { | 739 | grasp_servers |
| 740 | grasp_servers.iter().any(|s| { | 740 | .iter() |
| 741 | if let Ok(s) = normalize_grasp_server_url(s) { | 741 | .any(|s| s.trim_end_matches('/') == url.trim_end_matches('/')) |
| 742 | s == url | ||
| 743 | } else { | ||
| 744 | false | ||
| 745 | } | ||
| 746 | }) | ||
| 747 | } else { | ||
| 748 | false | ||
| 749 | } | ||
| 750 | } else { | 742 | } else { |
| 751 | false | 743 | false |
| 752 | } | 744 | } |
| 753 | } | 745 | } |
| 754 | 746 | ||
| 755 | pub fn is_grasp_server_clone_url(url: &str) -> bool { | 747 | pub fn is_grasp_server_clone_url(url: &str) -> bool { |
| 756 | extract_npub(url).is_ok() | 748 | // Must start with http:// or https:// |
| 757 | && (url.ends_with(".git") || url.ends_with(".git/")) | 749 | if !url.starts_with("http://") && !url.starts_with("https://") { |
| 758 | && url.starts_with("http") | 750 | return false; |
| 751 | } | ||
| 752 | |||
| 753 | // Must end with .git or .git/ | ||
| 754 | if !url.ends_with(".git") && !url.ends_with(".git/") { | ||
| 755 | return false; | ||
| 756 | } | ||
| 757 | |||
| 758 | // Must contain a valid npub | ||
| 759 | let npub = match extract_npub(url) { | ||
| 760 | Ok(npub) => npub, | ||
| 761 | Err(_) => return false, | ||
| 762 | }; | ||
| 763 | |||
| 764 | // Must have format: /{npub}/<repo-name>.git | ||
| 765 | // The npub must be followed by a slash and then a non-empty repo name | ||
| 766 | let npub_pattern = format!("/{}/", npub); | ||
| 767 | if let Some(npub_pos) = url.find(&npub_pattern) { | ||
| 768 | // Get the part after /{npub}/ | ||
| 769 | let after_npub = &url[npub_pos + npub_pattern.len()..]; | ||
| 770 | |||
| 771 | // Remove trailing slash if present | ||
| 772 | let after_npub = after_npub.trim_end_matches('/'); | ||
| 773 | |||
| 774 | // Must have a non-empty repo name that ends with .git | ||
| 775 | if after_npub.is_empty() || after_npub == ".git" { | ||
| 776 | return false; | ||
| 777 | } | ||
| 778 | |||
| 779 | // Repo name must be at least 1 character before .git | ||
| 780 | if !after_npub.ends_with(".git") { | ||
| 781 | return false; | ||
| 782 | } | ||
| 783 | |||
| 784 | let repo_name = &after_npub[..after_npub.len() - 4]; // Remove .git | ||
| 785 | !repo_name.is_empty() | ||
| 786 | } else { | ||
| 787 | false | ||
| 788 | } | ||
| 759 | } | 789 | } |
| 760 | 790 | ||
| 761 | pub fn format_grasp_server_url_as_relay_url(url: &str) -> Result<String> { | 791 | pub fn format_grasp_server_url_as_relay_url(url: &str) -> Result<String> { |
| @@ -1100,4 +1130,162 @@ mod tests { | |||
| 1100 | } | 1130 | } |
| 1101 | Ok(()) | 1131 | Ok(()) |
| 1102 | } | 1132 | } |
| 1133 | |||
| 1134 | mod is_grasp_server_in_list { | ||
| 1135 | use super::*; | ||
| 1136 | |||
| 1137 | #[test] | ||
| 1138 | fn detects_in_list() { | ||
| 1139 | assert!(is_grasp_server_in_list( | ||
| 1140 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo.git", | ||
| 1141 | &[ | ||
| 1142 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo.git".to_string(), | ||
| 1143 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo2.git".to_string(), | ||
| 1144 | ], | ||
| 1145 | )) | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | #[test] | ||
| 1149 | fn ignores_not_in_list() { | ||
| 1150 | assert!(!is_grasp_server_in_list( | ||
| 1151 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo3.git", | ||
| 1152 | &[ | ||
| 1153 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo.git".to_string(), | ||
| 1154 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/example-repo2.git".to_string(), | ||
| 1155 | ], | ||
| 1156 | )) | ||
| 1157 | } | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | mod is_grasp_server_clone_url { | ||
| 1161 | use super::*; | ||
| 1162 | |||
| 1163 | #[test] | ||
| 1164 | fn valid_https_url() { | ||
| 1165 | assert!(is_grasp_server_clone_url( | ||
| 1166 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1167 | )); | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | #[test] | ||
| 1171 | fn valid_http_url() { | ||
| 1172 | assert!(is_grasp_server_clone_url( | ||
| 1173 | "http://localhost:8080/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/test-repo.git" | ||
| 1174 | )); | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | #[test] | ||
| 1178 | fn valid_with_trailing_slash() { | ||
| 1179 | assert!(is_grasp_server_clone_url( | ||
| 1180 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git/" | ||
| 1181 | )); | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | #[test] | ||
| 1185 | fn valid_with_nested_path() { | ||
| 1186 | assert!(is_grasp_server_clone_url( | ||
| 1187 | "https://relay.ngit.dev/path/to/server/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1188 | )); | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | #[test] | ||
| 1192 | fn valid_with_port() { | ||
| 1193 | assert!(is_grasp_server_clone_url( | ||
| 1194 | "https://relay.ngit.dev:8080/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1195 | )); | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | #[test] | ||
| 1199 | fn invalid_missing_git_extension() { | ||
| 1200 | assert!(!is_grasp_server_clone_url( | ||
| 1201 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo" | ||
| 1202 | )); | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | #[test] | ||
| 1206 | fn invalid_no_npub() { | ||
| 1207 | assert!(!is_grasp_server_clone_url( | ||
| 1208 | "https://relay.ngit.dev/my-repo.git" | ||
| 1209 | )); | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | #[test] | ||
| 1213 | fn invalid_npub_not_in_path() { | ||
| 1214 | // npub exists but not in the path structure (e.g., in query string or fragment) | ||
| 1215 | assert!(!is_grasp_server_clone_url( | ||
| 1216 | "https://relay.ngit.dev/my-repo.git?npub=npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr" | ||
| 1217 | )); | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | #[test] | ||
| 1221 | fn invalid_wrong_protocol() { | ||
| 1222 | assert!(!is_grasp_server_clone_url( | ||
| 1223 | "ftp://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1224 | )); | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | #[test] | ||
| 1228 | fn invalid_no_protocol() { | ||
| 1229 | assert!(!is_grasp_server_clone_url( | ||
| 1230 | "relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1231 | )); | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | #[test] | ||
| 1235 | fn invalid_wss_protocol() { | ||
| 1236 | assert!(!is_grasp_server_clone_url( | ||
| 1237 | "wss://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-repo.git" | ||
| 1238 | )); | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | #[test] | ||
| 1242 | fn invalid_npub_not_followed_by_slash() { | ||
| 1243 | // npub must be followed by a slash before the repo name | ||
| 1244 | assert!(!is_grasp_server_clone_url( | ||
| 1245 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejrmy-repo.git" | ||
| 1246 | )); | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | #[test] | ||
| 1250 | fn invalid_no_repo_name_after_npub() { | ||
| 1251 | assert!(!is_grasp_server_clone_url( | ||
| 1252 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/.git" | ||
| 1253 | )); | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | #[test] | ||
| 1257 | fn invalid_empty_repo_name() { | ||
| 1258 | assert!(!is_grasp_server_clone_url( | ||
| 1259 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr.git" | ||
| 1260 | )); | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | #[test] | ||
| 1264 | fn invalid_malformed_npub() { | ||
| 1265 | assert!(!is_grasp_server_clone_url( | ||
| 1266 | "https://relay.ngit.dev/npub123invalid/my-repo.git" | ||
| 1267 | )); | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | #[test] | ||
| 1271 | fn valid_repo_name_with_hyphens() { | ||
| 1272 | assert!(is_grasp_server_clone_url( | ||
| 1273 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my-awesome-repo.git" | ||
| 1274 | )); | ||
| 1275 | } | ||
| 1276 | |||
| 1277 | #[test] | ||
| 1278 | fn valid_repo_name_with_underscores() { | ||
| 1279 | assert!(is_grasp_server_clone_url( | ||
| 1280 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/my_repo.git" | ||
| 1281 | )); | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | #[test] | ||
| 1285 | fn valid_repo_name_with_numbers() { | ||
| 1286 | assert!(is_grasp_server_clone_url( | ||
| 1287 | "https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/repo123.git" | ||
| 1288 | )); | ||
| 1289 | } | ||
| 1290 | } | ||
| 1103 | } | 1291 | } |