diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-08 11:20:35 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-08 11:23:28 +0000 |
| commit | 5d02ad6b893f9059044914c115d77cf9d8e589c3 (patch) | |
| tree | b727f9c44d2f2d4e203dc2344e4c9bd5144a77dd /src/nostr | |
| parent | 075307804bf66bba10f5bc55cb40e2e6a98a65ee (diff) | |
refactor: replace hardcoded Kind constants with rust-nostr variants
- Replace KIND_REPOSITORY_ANNOUNCEMENT with Kind::GitRepoAnnouncement
- Replace KIND_REPOSITORY_STATE with Kind::RepoState
- Replace KIND_PR with Kind::GitPullRequest
- Replace KIND_PR_UPDATE with Kind::GitPullRequestUpdate
- Replace KIND_USER_GRASP_LIST with Kind::GitUserGraspList
- Replace KIND_PATCH with Kind::GitPatch
- Replace KIND_ISSUE with Kind::GitIssue
- Replace KIND_COMMENT with Kind::Comment
- Replace all Kind::Custom(30617|30618|1617|1618|1619|1621|1111|10317) patterns
- Remove all hardcoded KIND_* constants from events.rs
- Update all match statements to use Kind enum directly
- Update all filter builders to use Kind variants
- Update all test helpers and assertions
Benefits:
- Type safety: compiler prevents wrong kind numbers
- Readability: Kind::GitRepoAnnouncement is self-documenting
- Maintainability: single source of truth (rust-nostr)
- IDE support: full autocompletion and refactoring
- Standards: aligns with rust-nostr best practices
Files modified: 21
Constants removed: 9
Patterns replaced: 100+
Tests passing: 222/222
Diffstat (limited to 'src/nostr')
| -rw-r--r-- | src/nostr/builder.rs | 17 | ||||
| -rw-r--r-- | src/nostr/events.rs | 56 | ||||
| -rw-r--r-- | src/nostr/policy/announcement.rs | 14 |
3 files changed, 37 insertions, 50 deletions
diff --git a/src/nostr/builder.rs b/src/nostr/builder.rs index 81f7fbb..939ccef 100644 --- a/src/nostr/builder.rs +++ b/src/nostr/builder.rs | |||
| @@ -12,10 +12,7 @@ use nostr_lmdb::NostrLmdb; | |||
| 12 | use nostr_relay_builder::prelude::*; | 12 | use nostr_relay_builder::prelude::*; |
| 13 | 13 | ||
| 14 | use crate::config::{Config, DatabaseBackend}; | 14 | use crate::config::{Config, DatabaseBackend}; |
| 15 | use crate::nostr::events::{ | 15 | use crate::nostr::events::RepositoryAnnouncement; |
| 16 | RepositoryAnnouncement, KIND_PR, KIND_PR_UPDATE, KIND_REPOSITORY_ANNOUNCEMENT, | ||
| 17 | KIND_REPOSITORY_STATE, KIND_USER_GRASP_LIST, | ||
| 18 | }; | ||
| 19 | use crate::nostr::policy::{ | 16 | use crate::nostr::policy::{ |
| 20 | AnnouncementPolicy, AnnouncementResult, PolicyContext, PrEventPolicy, ReferenceResult, | 17 | AnnouncementPolicy, AnnouncementResult, PolicyContext, PrEventPolicy, ReferenceResult, |
| 21 | RelatedEventPolicy, StatePolicy, StateResult, | 18 | RelatedEventPolicy, StatePolicy, StateResult, |
| @@ -377,11 +374,13 @@ impl WritePolicy for Nip34WritePolicy { | |||
| 377 | // Sync uses localhost:0 as a dummy address | 374 | // Sync uses localhost:0 as a dummy address |
| 378 | let is_synced = addr.ip().is_loopback() && addr.port() == 0; | 375 | let is_synced = addr.ip().is_loopback() && addr.port() == 0; |
| 379 | 376 | ||
| 380 | match event.kind.as_u16() { | 377 | match event.kind { |
| 381 | KIND_REPOSITORY_ANNOUNCEMENT => self.handle_announcement(event).await, | 378 | Kind::GitRepoAnnouncement => self.handle_announcement(event).await, |
| 382 | KIND_REPOSITORY_STATE => self.handle_state(event, is_synced).await, | 379 | Kind::RepoState => self.handle_state(event, is_synced).await, |
| 383 | KIND_PR | KIND_PR_UPDATE => self.handle_pr_event(event, is_synced).await, | 380 | Kind::GitPullRequest | Kind::GitPullRequestUpdate => { |
| 384 | KIND_USER_GRASP_LIST => { | 381 | self.handle_pr_event(event, is_synced).await |
| 382 | } | ||
| 383 | Kind::GitUserGraspList => { | ||
| 385 | // Accept all kind 10317 (User Grasp List) events | 384 | // Accept all kind 10317 (User Grasp List) events |
| 386 | // for better GRASP repository discovery | 385 | // for better GRASP repository discovery |
| 387 | tracing::debug!( | 386 | tracing::debug!( |
diff --git a/src/nostr/events.rs b/src/nostr/events.rs index 1fcb75e..4f7c907 100644 --- a/src/nostr/events.rs +++ b/src/nostr/events.rs | |||
| @@ -9,20 +9,12 @@ | |||
| 9 | use anyhow::{anyhow, Result}; | 9 | use anyhow::{anyhow, Result}; |
| 10 | use nostr_sdk::{Event, Kind, TagKind, ToBech32}; | 10 | use nostr_sdk::{Event, Kind, TagKind, ToBech32}; |
| 11 | 11 | ||
| 12 | /// NIP-34 Repository Announcement (kind 30617) | 12 | // NOTE: Using rust-nostr Kind variants instead of hardcoded constants: |
| 13 | pub const KIND_REPOSITORY_ANNOUNCEMENT: u16 = 30617; | 13 | // - KIND_REPOSITORY_ANNOUNCEMENT -> Kind::GitRepoAnnouncement (30617) |
| 14 | 14 | // - KIND_REPOSITORY_STATE -> Kind::RepoState (30618) | |
| 15 | /// NIP-34 Repository State Announcement (kind 30618) | 15 | // - KIND_PR -> Kind::GitPullRequest (1618) |
| 16 | pub const KIND_REPOSITORY_STATE: u16 = 30618; | 16 | // - KIND_PR_UPDATE -> Kind::GitPullRequestUpdate (1619) |
| 17 | 17 | // - KIND_USER_GRASP_LIST -> Kind::GitUserGraspList (10317) | |
| 18 | /// NIP-34 Pull Request (kind 1618) - has `c` tag for commit | ||
| 19 | pub const KIND_PR: u16 = 1618; | ||
| 20 | |||
| 21 | /// NIP-34 Pull Request Update (kind 1619) - has `c` tag for commit | ||
| 22 | pub const KIND_PR_UPDATE: u16 = 1619; | ||
| 23 | |||
| 24 | /// User Grasp List (kind 10317) - user's personal list of GRASP repositories | ||
| 25 | pub const KIND_USER_GRASP_LIST: u16 = 10317; | ||
| 26 | 18 | ||
| 27 | /// Repository announcement details extracted from NIP-34 event | 19 | /// Repository announcement details extracted from NIP-34 event |
| 28 | #[derive(Debug, Clone)] | 20 | #[derive(Debug, Clone)] |
| @@ -40,10 +32,10 @@ pub struct RepositoryAnnouncement { | |||
| 40 | impl RepositoryAnnouncement { | 32 | impl RepositoryAnnouncement { |
| 41 | /// Parse a repository announcement from a NIP-34 kind 30617 event | 33 | /// Parse a repository announcement from a NIP-34 kind 30617 event |
| 42 | pub fn from_event(event: Event) -> Result<Self> { | 34 | pub fn from_event(event: Event) -> Result<Self> { |
| 43 | if event.kind != Kind::from(KIND_REPOSITORY_ANNOUNCEMENT) { | 35 | if event.kind != Kind::GitRepoAnnouncement { |
| 44 | return Err(anyhow!( | 36 | return Err(anyhow!( |
| 45 | "Invalid event kind: expected {}, got {}", | 37 | "Invalid event kind: expected {}, got {}", |
| 46 | KIND_REPOSITORY_ANNOUNCEMENT, | 38 | Kind::GitRepoAnnouncement, |
| 47 | event.kind | 39 | event.kind |
| 48 | )); | 40 | )); |
| 49 | } | 41 | } |
| @@ -197,10 +189,10 @@ pub struct TagState { | |||
| 197 | impl RepositoryState { | 189 | impl RepositoryState { |
| 198 | /// Parse a repository state from a NIP-34 kind 30618 event | 190 | /// Parse a repository state from a NIP-34 kind 30618 event |
| 199 | pub fn from_event(event: Event) -> Result<Self> { | 191 | pub fn from_event(event: Event) -> Result<Self> { |
| 200 | if event.kind != Kind::from(KIND_REPOSITORY_STATE) { | 192 | if event.kind != Kind::RepoState { |
| 201 | return Err(anyhow!( | 193 | return Err(anyhow!( |
| 202 | "Invalid event kind: expected {}, got {}", | 194 | "Invalid event kind: expected {}, got {}", |
| 203 | KIND_REPOSITORY_STATE, | 195 | Kind::RepoState, |
| 204 | event.kind | 196 | event.kind |
| 205 | )); | 197 | )); |
| 206 | } | 198 | } |
| @@ -346,10 +338,10 @@ impl RepositoryState { | |||
| 346 | /// Returns Ok(()) if valid, Err with reason if invalid. | 338 | /// Returns Ok(()) if valid, Err with reason if invalid. |
| 347 | pub fn validate_announcement(event: &Event, domain: &str) -> Result<()> { | 339 | pub fn validate_announcement(event: &Event, domain: &str) -> Result<()> { |
| 348 | // Must be kind 30617 | 340 | // Must be kind 30617 |
| 349 | if event.kind != Kind::from(KIND_REPOSITORY_ANNOUNCEMENT) { | 341 | if event.kind != Kind::GitRepoAnnouncement { |
| 350 | return Err(anyhow!( | 342 | return Err(anyhow!( |
| 351 | "Invalid kind: expected {}", | 343 | "Invalid kind: expected {}", |
| 352 | KIND_REPOSITORY_ANNOUNCEMENT | 344 | Kind::GitRepoAnnouncement |
| 353 | )); | 345 | )); |
| 354 | } | 346 | } |
| 355 | 347 | ||
| @@ -381,8 +373,8 @@ pub fn validate_announcement(event: &Event, domain: &str) -> Result<()> { | |||
| 381 | /// Returns Ok(()) if valid, Err with reason if invalid. | 373 | /// Returns Ok(()) if valid, Err with reason if invalid. |
| 382 | pub fn validate_state(event: &Event) -> Result<()> { | 374 | pub fn validate_state(event: &Event) -> Result<()> { |
| 383 | // Must be kind 30618 | 375 | // Must be kind 30618 |
| 384 | if event.kind != Kind::from(KIND_REPOSITORY_STATE) { | 376 | if event.kind != Kind::RepoState { |
| 385 | return Err(anyhow!("Invalid kind: expected {}", KIND_REPOSITORY_STATE)); | 377 | return Err(anyhow!("Invalid kind: expected {}", Kind::RepoState)); |
| 386 | } | 378 | } |
| 387 | 379 | ||
| 388 | // Must have identifier | 380 | // Must have identifier |
| @@ -433,7 +425,7 @@ mod tests { | |||
| 433 | )); | 425 | )); |
| 434 | } | 426 | } |
| 435 | 427 | ||
| 436 | EventBuilder::new(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), "Test repository") | 428 | EventBuilder::new(Kind::GitRepoAnnouncement, "Test repository") |
| 437 | .tags(tags) | 429 | .tags(tags) |
| 438 | .sign_with_keys(keys) | 430 | .sign_with_keys(keys) |
| 439 | .unwrap() | 431 | .unwrap() |
| @@ -454,7 +446,7 @@ mod tests { | |||
| 454 | )); | 446 | )); |
| 455 | } | 447 | } |
| 456 | 448 | ||
| 457 | EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "") | 449 | EventBuilder::new(Kind::RepoState, "") |
| 458 | .tags(tags) | 450 | .tags(tags) |
| 459 | .sign_with_keys(keys) | 451 | .sign_with_keys(keys) |
| 460 | .unwrap() | 452 | .unwrap() |
| @@ -483,7 +475,7 @@ mod tests { | |||
| 483 | #[test] | 475 | #[test] |
| 484 | fn test_parse_announcement_missing_identifier() { | 476 | fn test_parse_announcement_missing_identifier() { |
| 485 | let keys = create_test_keys(); | 477 | let keys = create_test_keys(); |
| 486 | let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), "Test repository") | 478 | let event = EventBuilder::new(Kind::GitRepoAnnouncement, "Test repository") |
| 487 | .sign_with_keys(&keys) | 479 | .sign_with_keys(&keys) |
| 488 | .unwrap(); | 480 | .unwrap(); |
| 489 | 481 | ||
| @@ -579,7 +571,7 @@ mod tests { | |||
| 579 | #[test] | 571 | #[test] |
| 580 | fn test_validate_state_missing_identifier() { | 572 | fn test_validate_state_missing_identifier() { |
| 581 | let keys = create_test_keys(); | 573 | let keys = create_test_keys(); |
| 582 | let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "") | 574 | let event = EventBuilder::new(Kind::RepoState, "") |
| 583 | .sign_with_keys(&keys) | 575 | .sign_with_keys(&keys) |
| 584 | .unwrap(); | 576 | .unwrap(); |
| 585 | 577 | ||
| @@ -614,7 +606,7 @@ mod tests { | |||
| 614 | vec![maintainer_keys.public_key().to_hex()], | 606 | vec![maintainer_keys.public_key().to_hex()], |
| 615 | )); | 607 | )); |
| 616 | 608 | ||
| 617 | let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT), "Test repository") | 609 | let event = EventBuilder::new(Kind::GitRepoAnnouncement, "Test repository") |
| 618 | .tags(tags) | 610 | .tags(tags) |
| 619 | .sign_with_keys(&keys) | 611 | .sign_with_keys(&keys) |
| 620 | .unwrap(); | 612 | .unwrap(); |
| @@ -649,7 +641,7 @@ mod tests { | |||
| 649 | vec!["e5f6g7h8".to_string()], | 641 | vec!["e5f6g7h8".to_string()], |
| 650 | )); | 642 | )); |
| 651 | 643 | ||
| 652 | let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "") | 644 | let event = EventBuilder::new(Kind::RepoState, "") |
| 653 | .tags(tags) | 645 | .tags(tags) |
| 654 | .sign_with_keys(&keys) | 646 | .sign_with_keys(&keys) |
| 655 | .unwrap(); | 647 | .unwrap(); |
| @@ -683,7 +675,7 @@ mod tests { | |||
| 683 | vec!["ref: refs/heads/main".to_string()], | 675 | vec!["ref: refs/heads/main".to_string()], |
| 684 | )); | 676 | )); |
| 685 | 677 | ||
| 686 | let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "") | 678 | let event = EventBuilder::new(Kind::RepoState, "") |
| 687 | .tags(tags) | 679 | .tags(tags) |
| 688 | .sign_with_keys(&keys) | 680 | .sign_with_keys(&keys) |
| 689 | .unwrap(); | 681 | .unwrap(); |
| @@ -716,7 +708,7 @@ mod tests { | |||
| 716 | vec!["refs/heads/develop".to_string()], | 708 | vec!["refs/heads/develop".to_string()], |
| 717 | )); | 709 | )); |
| 718 | 710 | ||
| 719 | let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "") | 711 | let event = EventBuilder::new(Kind::RepoState, "") |
| 720 | .tags(tags) | 712 | .tags(tags) |
| 721 | .sign_with_keys(&keys) | 713 | .sign_with_keys(&keys) |
| 722 | .unwrap(); | 714 | .unwrap(); |
| @@ -740,7 +732,7 @@ mod tests { | |||
| 740 | ), | 732 | ), |
| 741 | ]; | 733 | ]; |
| 742 | 734 | ||
| 743 | let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "") | 735 | let event = EventBuilder::new(Kind::RepoState, "") |
| 744 | .tags(tags) | 736 | .tags(tags) |
| 745 | .sign_with_keys(&keys) | 737 | .sign_with_keys(&keys) |
| 746 | .unwrap(); | 738 | .unwrap(); |
| @@ -773,7 +765,7 @@ mod tests { | |||
| 773 | vec!["refs/heads/develop".to_string()], | 765 | vec!["refs/heads/develop".to_string()], |
| 774 | )); | 766 | )); |
| 775 | 767 | ||
| 776 | let event = EventBuilder::new(Kind::from(KIND_REPOSITORY_STATE), "") | 768 | let event = EventBuilder::new(Kind::RepoState, "") |
| 777 | .tags(tags) | 769 | .tags(tags) |
| 778 | .sign_with_keys(&keys) | 770 | .sign_with_keys(&keys) |
| 779 | .unwrap(); | 771 | .unwrap(); |
diff --git a/src/nostr/policy/announcement.rs b/src/nostr/policy/announcement.rs index 353738b..61840fb 100644 --- a/src/nostr/policy/announcement.rs +++ b/src/nostr/policy/announcement.rs | |||
| @@ -5,9 +5,7 @@ | |||
| 5 | use nostr_relay_builder::prelude::{Alphabet, Event, Filter, Kind, PublicKey, SingleLetterTag}; | 5 | use nostr_relay_builder::prelude::{Alphabet, Event, Filter, Kind, PublicKey, SingleLetterTag}; |
| 6 | 6 | ||
| 7 | use super::PolicyContext; | 7 | use super::PolicyContext; |
| 8 | use crate::nostr::events::{ | 8 | use crate::nostr::events::{validate_announcement, RepositoryAnnouncement}; |
| 9 | validate_announcement, RepositoryAnnouncement, KIND_REPOSITORY_ANNOUNCEMENT, | ||
| 10 | }; | ||
| 11 | 9 | ||
| 12 | /// Result of announcement policy evaluation | 10 | /// Result of announcement policy evaluation |
| 13 | #[derive(Debug)] | 11 | #[derive(Debug)] |
| @@ -121,12 +119,10 @@ impl AnnouncementPolicy { | |||
| 121 | author: &PublicKey, | 119 | author: &PublicKey, |
| 122 | ) -> Result<bool, String> { | 120 | ) -> Result<bool, String> { |
| 123 | // Query all announcements with this identifier that are already in the database | 121 | // Query all announcements with this identifier that are already in the database |
| 124 | let filter = Filter::new() | 122 | let filter = Filter::new().kind(Kind::GitRepoAnnouncement).custom_tag( |
| 125 | .kind(Kind::from(KIND_REPOSITORY_ANNOUNCEMENT)) | 123 | SingleLetterTag::lowercase(Alphabet::D), |
| 126 | .custom_tag( | 124 | identifier.to_string(), |
| 127 | SingleLetterTag::lowercase(Alphabet::D), | 125 | ); |
| 128 | identifier.to_string(), | ||
| 129 | ); | ||
| 130 | 126 | ||
| 131 | let announcements: Vec<Event> = match self.ctx.database.query(filter).await { | 127 | let announcements: Vec<Event> = match self.ctx.database.query(filter).await { |
| 132 | Ok(events) => events.into_iter().collect(), | 128 | Ok(events) => events.into_iter().collect(), |