upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/purgatory/sync/context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/purgatory/sync/context.rs')
-rw-r--r--src/purgatory/sync/context.rs77
1 files changed, 75 insertions, 2 deletions
diff --git a/src/purgatory/sync/context.rs b/src/purgatory/sync/context.rs
index e97b708..2922f10 100644
--- a/src/purgatory/sync/context.rs
+++ b/src/purgatory/sync/context.rs
@@ -63,6 +63,18 @@ impl ProcessResult {
63/// with mocks. 63/// with mocks.
64#[async_trait] 64#[async_trait]
65pub trait SyncContext: Send + Sync { 65pub trait SyncContext: Send + Sync {
66 /// Collect clone URLs from PR events in purgatory for a given identifier.
67 ///
68 /// PR events (kind 1618) and PR Update events (kind 1619) can include `clone` tags
69 /// specifying where the PR commits can be fetched from. This method extracts those
70 /// URLs to supplement the clone URLs from repository announcements.
71 ///
72 /// # Arguments
73 /// * `identifier` - The repository identifier
74 ///
75 /// # Returns
76 /// Set of clone URLs from PR events in purgatory for this identifier
77 fn collect_pr_clone_urls(&self, identifier: &str) -> HashSet<String>;
66 /// Get repository data (announcements, clone URLs, etc.) from the database. 78 /// Get repository data (announcements, clone URLs, etc.) from the database.
67 /// 79 ///
68 /// # Arguments 80 /// # Arguments
@@ -232,6 +244,30 @@ impl RealSyncContext {
232 244
233#[async_trait] 245#[async_trait]
234impl SyncContext for RealSyncContext { 246impl SyncContext for RealSyncContext {
247 fn collect_pr_clone_urls(&self, identifier: &str) -> HashSet<String> {
248 let mut urls = HashSet::new();
249
250 for entry in self.purgatory.find_prs_for_identifier(identifier) {
251 if let Some(ref event) = entry.event {
252 for tag in event.tags.iter() {
253 let tag_vec = tag.clone().to_vec();
254 if tag_vec.len() >= 2 && tag_vec[0] == "clone" {
255 // Clone tags can have multiple URLs: ["clone", "url1", "url2", ...]
256 urls.extend(tag_vec[1..].iter().cloned());
257 }
258 }
259 }
260 }
261
262 debug!(
263 identifier = %identifier,
264 pr_clone_urls_count = urls.len(),
265 "Collected clone URLs from PR events in purgatory"
266 );
267
268 urls
269 }
270
235 async fn fetch_repository_data(&self, identifier: &str) -> Result<RepositoryData> { 271 async fn fetch_repository_data(&self, identifier: &str) -> Result<RepositoryData> {
236 crate::git::authorization::fetch_repository_data(&self.database, identifier).await 272 crate::git::authorization::fetch_repository_data(&self.database, identifier).await
237 } 273 }
@@ -450,9 +486,12 @@ pub mod mock {
450 /// Repository data to return from fetch_repository_data 486 /// Repository data to return from fetch_repository_data
451 repo_data: RwLock<Option<RepositoryData>>, 487 repo_data: RwLock<Option<RepositoryData>>,
452 488
453 /// Clone URLs available for the repository 489 /// Clone URLs available for the repository (from announcements)
454 clone_urls: Vec<String>, 490 clone_urls: Vec<String>,
455 491
492 /// Clone URLs from PR events in purgatory
493 pr_clone_urls: HashSet<String>,
494
456 /// OIDs still needed (decremented when "fetched") 495 /// OIDs still needed (decremented when "fetched")
457 needed_oids: RwLock<HashSet<String>>, 496 needed_oids: RwLock<HashSet<String>>,
458 497
@@ -490,6 +529,7 @@ pub mod mock {
490 Self { 529 Self {
491 repo_data: RwLock::new(None), 530 repo_data: RwLock::new(None),
492 clone_urls: Vec::new(), 531 clone_urls: Vec::new(),
532 pr_clone_urls: HashSet::new(),
493 needed_oids: RwLock::new(HashSet::new()), 533 needed_oids: RwLock::new(HashSet::new()),
494 url_provides_oids: HashMap::new(), 534 url_provides_oids: HashMap::new(),
495 fetch_log: RwLock::new(Vec::new()), 535 fetch_log: RwLock::new(Vec::new()),
@@ -501,12 +541,18 @@ pub mod mock {
501 } 541 }
502 } 542 }
503 543
504 /// Configure clone URLs for the repository. 544 /// Configure clone URLs for the repository (from announcements).
505 pub fn with_urls(mut self, urls: &[&str]) -> Self { 545 pub fn with_urls(mut self, urls: &[&str]) -> Self {
506 self.clone_urls = urls.iter().map(|s| s.to_string()).collect(); 546 self.clone_urls = urls.iter().map(|s| s.to_string()).collect();
507 self 547 self
508 } 548 }
509 549
550 /// Configure clone URLs from PR events in purgatory.
551 pub fn with_pr_clone_urls(mut self, urls: &[&str]) -> Self {
552 self.pr_clone_urls = urls.iter().map(|s| s.to_string()).collect();
553 self
554 }
555
510 /// Configure OIDs that are still needed. 556 /// Configure OIDs that are still needed.
511 pub fn with_needed_oids(self, oids: &[&str]) -> Self { 557 pub fn with_needed_oids(self, oids: &[&str]) -> Self {
512 *self.needed_oids.write().unwrap() = oids.iter().map(|s| s.to_string()).collect(); 558 *self.needed_oids.write().unwrap() = oids.iter().map(|s| s.to_string()).collect();
@@ -580,6 +626,10 @@ pub mod mock {
580 626
581 #[async_trait] 627 #[async_trait]
582 impl SyncContext for MockSyncContext { 628 impl SyncContext for MockSyncContext {
629 fn collect_pr_clone_urls(&self, _identifier: &str) -> HashSet<String> {
630 self.pr_clone_urls.clone()
631 }
632
583 async fn fetch_repository_data(&self, _identifier: &str) -> Result<RepositoryData> { 633 async fn fetch_repository_data(&self, _identifier: &str) -> Result<RepositoryData> {
584 // Return stored repo_data or create a minimal one with clone URLs 634 // Return stored repo_data or create a minimal one with clone URLs
585 if let Some(data) = self.repo_data.read().unwrap().as_ref() { 635 if let Some(data) = self.repo_data.read().unwrap().as_ref() {
@@ -791,5 +841,28 @@ pub mod mock {
791 mock.set_pending_events(false); 841 mock.set_pending_events(false);
792 assert!(!mock.has_pending_events("test-repo")); 842 assert!(!mock.has_pending_events("test-repo"));
793 } 843 }
844
845 #[test]
846 fn mock_collect_pr_clone_urls_returns_configured_urls() {
847 let mock = MockSyncContext::new().with_pr_clone_urls(&[
848 "https://fork-server.com/repo.git",
849 "https://another-fork.com/repo.git",
850 ]);
851
852 let urls = mock.collect_pr_clone_urls("any-identifier");
853
854 assert_eq!(urls.len(), 2);
855 assert!(urls.contains("https://fork-server.com/repo.git"));
856 assert!(urls.contains("https://another-fork.com/repo.git"));
857 }
858
859 #[test]
860 fn mock_collect_pr_clone_urls_empty_by_default() {
861 let mock = MockSyncContext::new();
862
863 let urls = mock.collect_pr_clone_urls("any-identifier");
864
865 assert!(urls.is_empty());
866 }
794 } 867 }
795} 868}