commit 23e1e673900138b85dddd86b21a1db7dfce40eba parent 0570212e816f8b51e97c711173b6f42e9128c82c Author: triesap <tyson@radroots.org> Date: Fri, 26 Dec 2025 15:15:14 +0000 events: centralize nostr kind constants - Replace hardcoded kind literals with radroots_events::kinds constants - Add exported KIND_* constants to Rust kinds modules and TS bindings - Align codec encode/decode DEFAULT_KIND checks and error expectations - Update tests and trade listing validation to use shared kind constants Diffstat:
38 files changed, 220 insertions(+), 111 deletions(-)
diff --git a/events-codec/src/app_data/encode.rs b/events-codec/src/app_data/encode.rs @@ -1,5 +1,5 @@ #[cfg(not(feature = "std"))] -use alloc::{string::String, vec::Vec}; +use alloc::{string::{String, ToString}, vec, vec::Vec}; use radroots_events::app_data::{RadrootsAppData, KIND_APP_DATA}; use radroots_events::tags::TAG_D; diff --git a/events-codec/src/comment/decode.rs b/events-codec/src/comment/decode.rs @@ -4,13 +4,14 @@ use alloc::{string::{String, ToString}, vec::Vec}; use radroots_events::{ RadrootsNostrEvent, comment::{RadrootsComment, RadrootsCommentEventIndex, RadrootsCommentEventMetadata}, + kinds::KIND_COMMENT, tags::{TAG_E_PREV, TAG_E_ROOT}, }; use crate::error::EventParseError; use crate::event_ref::{find_event_ref_tag, parse_event_ref_tag, parse_nip10_ref_tags}; -const DEFAULT_KIND: u32 = 1; +const DEFAULT_KIND: u32 = KIND_COMMENT; pub fn comment_from_tags( kind: u32, @@ -19,7 +20,7 @@ pub fn comment_from_tags( ) -> Result<RadrootsComment, EventParseError> { if kind != DEFAULT_KIND { return Err(EventParseError::InvalidKind { - expected: "1", + expected: "1111", got: kind, }); } diff --git a/events-codec/src/comment/encode.rs b/events-codec/src/comment/encode.rs @@ -9,8 +9,9 @@ use radroots_events::{ use crate::error::EventEncodeError; use crate::event_ref::push_nip10_ref_tags; use crate::wire::WireEventParts; +use radroots_events::kinds::KIND_COMMENT; -const DEFAULT_KIND: u32 = 1; +const DEFAULT_KIND: u32 = KIND_COMMENT; fn validate_ref( event: &RadrootsNostrEventRef, diff --git a/events-codec/src/follow/decode.rs b/events-codec/src/follow/decode.rs @@ -4,11 +4,12 @@ use alloc::{string::String, vec::Vec}; use radroots_events::{ RadrootsNostrEvent, follow::{RadrootsFollow, RadrootsFollowEventIndex, RadrootsFollowEventMetadata, RadrootsFollowProfile}, + kinds::KIND_FOLLOW, }; use crate::error::EventParseError; -const DEFAULT_KIND: u32 = 3; +const DEFAULT_KIND: u32 = KIND_FOLLOW; fn looks_like_ws_relay(s: &str) -> bool { s.starts_with("ws://") || s.starts_with("wss://") diff --git a/events-codec/src/follow/encode.rs b/events-codec/src/follow/encode.rs @@ -5,8 +5,9 @@ use radroots_events::follow::{RadrootsFollow, RadrootsFollowProfile}; use crate::error::EventEncodeError; use crate::wire::WireEventParts; +use radroots_events::kinds::KIND_FOLLOW; -const DEFAULT_KIND: u32 = 3; +const DEFAULT_KIND: u32 = KIND_FOLLOW; fn follow_tag(profile: &RadrootsFollowProfile) -> Result<Vec<String>, EventEncodeError> { if profile.public_key.trim().is_empty() { diff --git a/events-codec/src/listing/decode.rs b/events-codec/src/listing/decode.rs @@ -5,13 +5,14 @@ use alloc::{string::{String, ToString}, vec::Vec}; use radroots_events::{ RadrootsNostrEvent, + kinds::KIND_LISTING, listing::{RadrootsListing, RadrootsListingEventIndex, RadrootsListingEventMetadata}, tags::TAG_D, }; use crate::error::EventParseError; -const DEFAULT_KIND: u32 = 30402; +const DEFAULT_KIND: u32 = KIND_LISTING; fn parse_d_tag(tags: &[Vec<String>]) -> Result<String, EventParseError> { let tag = tags diff --git a/events-codec/src/listing/encode.rs b/events-codec/src/listing/encode.rs @@ -4,6 +4,8 @@ use alloc::vec::Vec; use alloc::string::String; use radroots_events::listing::RadrootsListing; +#[cfg(feature = "serde_json")] +use radroots_events::kinds::KIND_LISTING; use crate::error::EventEncodeError; use crate::listing::tags::listing_tags; @@ -11,7 +13,7 @@ use crate::listing::tags::listing_tags; use crate::wire::WireEventParts; #[cfg(feature = "serde_json")] -const DEFAULT_KIND: u32 = 30402; +const DEFAULT_KIND: u32 = KIND_LISTING; pub fn listing_build_tags(listing: &RadrootsListing) -> Result<Vec<Vec<String>>, EventEncodeError> { listing_tags(listing) diff --git a/events-codec/src/message/decode.rs b/events-codec/src/message/decode.rs @@ -7,11 +7,12 @@ use radroots_events::{ RadrootsMessage, RadrootsMessageEventIndex, RadrootsMessageEventMetadata, RadrootsMessageRecipient, }, + kinds::KIND_MESSAGE, }; use crate::error::EventParseError; -const DEFAULT_KIND: u32 = 14; +const DEFAULT_KIND: u32 = KIND_MESSAGE; fn parse_recipient_tag(tag: &[String]) -> Result<RadrootsMessageRecipient, EventParseError> { if tag.get(0).map(|s| s.as_str()) != Some("p") { diff --git a/events-codec/src/message/encode.rs b/events-codec/src/message/encode.rs @@ -2,11 +2,12 @@ use alloc::{string::{String, ToString}, vec::Vec}; use radroots_events::message::{RadrootsMessage, RadrootsMessageRecipient}; +use radroots_events::kinds::KIND_MESSAGE; use crate::error::EventEncodeError; use crate::wire::WireEventParts; -const DEFAULT_KIND: u32 = 14; +const DEFAULT_KIND: u32 = KIND_MESSAGE; fn validate_recipient(recipient: &RadrootsMessageRecipient) -> Result<(), EventEncodeError> { if recipient.public_key.trim().is_empty() { diff --git a/events-codec/src/post/decode.rs b/events-codec/src/post/decode.rs @@ -4,11 +4,12 @@ use alloc::{string::{String, ToString}, vec::Vec}; use radroots_events::{ RadrootsNostrEvent, post::{RadrootsPost, RadrootsPostEventIndex, RadrootsPostEventMetadata}, + kinds::KIND_POST, }; use crate::error::EventParseError; -const DEFAULT_KIND: u32 = 1; +const DEFAULT_KIND: u32 = KIND_POST; pub fn post_from_content(kind: u32, content: &str) -> Result<RadrootsPost, EventParseError> { if kind != DEFAULT_KIND { diff --git a/events-codec/src/post/encode.rs b/events-codec/src/post/encode.rs @@ -5,8 +5,9 @@ use radroots_events::post::RadrootsPost; use crate::error::EventEncodeError; use crate::wire::WireEventParts; +use radroots_events::kinds::KIND_POST; -const DEFAULT_KIND: u32 = 1; +const DEFAULT_KIND: u32 = KIND_POST; pub fn to_wire_parts(post: &RadrootsPost) -> Result<WireEventParts, EventEncodeError> { to_wire_parts_with_kind(post, DEFAULT_KIND) diff --git a/events-codec/src/profile/decode.rs b/events-codec/src/profile/decode.rs @@ -6,12 +6,13 @@ use alloc::{string::{String, ToString}, vec::Vec}; use radroots_events::{ RadrootsNostrEvent, profile::{RadrootsProfile, RadrootsProfileEventIndex, RadrootsProfileEventMetadata}, + kinds::KIND_PROFILE, }; use crate::error::EventParseError; use serde_json::Value; -const PROFILE_KIND: u32 = 0; +const PROFILE_KIND: u32 = KIND_PROFILE; fn parse_optional_string(value: &Value, key: &'static str) -> Option<String> { value.get(key).and_then(|v| v.as_str()).map(|s| s.to_string()) diff --git a/events-codec/src/profile/encode.rs b/events-codec/src/profile/encode.rs @@ -1,5 +1,6 @@ use crate::profile::error::ProfileEncodeError; use radroots_events::profile::RadrootsProfile; +use radroots_events::kinds::KIND_PROFILE; use nostr::Metadata; use nostr::prelude::Url; @@ -49,7 +50,7 @@ pub fn to_wire_parts(p: &RadrootsProfile) -> Result<WireEventParts, ProfileEncod let md = to_metadata(p)?; let content = serde_json::to_string(&md).map_err(|_| ProfileEncodeError::Json)?; Ok(WireEventParts { - kind: 0, + kind: KIND_PROFILE, content, tags: Vec::new(), }) diff --git a/events-codec/src/reaction/decode.rs b/events-codec/src/reaction/decode.rs @@ -4,13 +4,14 @@ use alloc::{string::{String, ToString}, vec::Vec}; use radroots_events::{ RadrootsNostrEvent, reaction::{RadrootsReaction, RadrootsReactionEventIndex, RadrootsReactionEventMetadata}, + kinds::KIND_REACTION, tags::TAG_E_ROOT, }; use crate::error::EventParseError; use crate::event_ref::{find_event_ref_tag, parse_event_ref_tag, parse_nip10_ref_tags}; -const DEFAULT_KIND: u32 = 7; +const DEFAULT_KIND: u32 = KIND_REACTION; pub fn reaction_from_tags( kind: u32, diff --git a/events-codec/src/reaction/encode.rs b/events-codec/src/reaction/encode.rs @@ -9,8 +9,9 @@ use radroots_events::{ use crate::error::EventEncodeError; use crate::event_ref::push_nip10_ref_tags; use crate::wire::WireEventParts; +use radroots_events::kinds::KIND_REACTION; -const DEFAULT_KIND: u32 = 7; +const DEFAULT_KIND: u32 = KIND_REACTION; fn validate_ref(event: &RadrootsNostrEventRef) -> Result<(), EventEncodeError> { if event.id.trim().is_empty() { diff --git a/events-codec/tests/app_data.rs b/events-codec/tests/app_data.rs @@ -1,4 +1,7 @@ -use radroots_events::app_data::{RadrootsAppData, KIND_APP_DATA}; +use radroots_events::{ + app_data::{RadrootsAppData, KIND_APP_DATA}, + kinds::KIND_POST, +}; use radroots_events_codec::app_data::decode::app_data_from_tags; use radroots_events_codec::app_data::encode::{app_data_build_tags, to_wire_parts}; use radroots_events_codec::error::{EventEncodeError, EventParseError}; @@ -33,12 +36,12 @@ fn app_data_to_wire_parts_sets_kind_tags_content() { #[test] fn app_data_from_tags_requires_kind() { let tags = vec![vec!["d".to_string(), "radroots.app".to_string()]]; - let err = app_data_from_tags(1, &tags, "payload").unwrap_err(); + let err = app_data_from_tags(KIND_POST, &tags, "payload").unwrap_err(); assert!(matches!( err, EventParseError::InvalidKind { expected: "30078", - got: 1 + got: KIND_POST } )); } diff --git a/events-codec/tests/comment.rs b/events-codec/tests/comment.rs @@ -1,6 +1,9 @@ mod common; -use radroots_events::comment::RadrootsComment; +use radroots_events::{ + comment::RadrootsComment, + kinds::{KIND_COMMENT, KIND_POST}, +}; use radroots_events::tags::{TAG_E_PREV, TAG_E_ROOT}; use radroots_events_codec::comment::decode::comment_from_tags; @@ -22,8 +25,8 @@ fn assert_event_ref_fields( #[test] fn comment_build_tags_requires_root_id() { let comment = RadrootsComment { - root: common::event_ref("", "author", 1), - parent: common::event_ref("parent", "author", 1), + root: common::event_ref("", "author", KIND_POST), + parent: common::event_ref("parent", "author", KIND_POST), content: "hello".to_string(), }; @@ -37,8 +40,8 @@ fn comment_build_tags_requires_root_id() { #[test] fn comment_build_tags_requires_parent_author() { let comment = RadrootsComment { - root: common::event_ref("root", "author", 1), - parent: common::event_ref("parent", "", 1), + root: common::event_ref("root", "author", KIND_POST), + parent: common::event_ref("parent", "", KIND_POST), content: "hello".to_string(), }; @@ -52,8 +55,8 @@ fn comment_build_tags_requires_parent_author() { #[test] fn comment_to_wire_parts_requires_content() { let comment = RadrootsComment { - root: common::event_ref("root", "author", 1), - parent: common::event_ref("parent", "author", 1), + root: common::event_ref("root", "author", KIND_POST), + parent: common::event_ref("parent", "author", KIND_POST), content: " ".to_string(), }; @@ -69,14 +72,14 @@ fn comment_roundtrip_from_tags_with_parent() { let root = common::event_ref_with_d( "root", "author", - 1, + KIND_POST, "root-d", Some(vec!["wss://relay".to_string()]), ); let parent = common::event_ref_with_d( "parent", "author", - 1, + KIND_POST, "parent-d", Some(vec!["wss://relay-2".to_string()]), ); @@ -85,7 +88,7 @@ fn comment_roundtrip_from_tags_with_parent() { push_nip10_ref_tags(&mut tags, &root, "E", "P", "K", "A"); push_nip10_ref_tags(&mut tags, &parent, "e", "p", "k", "a"); - let comment = comment_from_tags(1, &tags, "hello").unwrap(); + let comment = comment_from_tags(KIND_COMMENT, &tags, "hello").unwrap(); assert_event_ref_fields(&comment.root, &root); assert_event_ref_fields(&comment.parent, &parent); @@ -94,11 +97,11 @@ fn comment_roundtrip_from_tags_with_parent() { #[test] fn comment_from_tags_defaults_parent_to_root() { - let root = common::event_ref("root", "author", 1); + let root = common::event_ref("root", "author", KIND_POST); let mut tags = Vec::new(); push_nip10_ref_tags(&mut tags, &root, "E", "P", "K", "A"); - let comment = comment_from_tags(1, &tags, "hello").unwrap(); + let comment = comment_from_tags(KIND_COMMENT, &tags, "hello").unwrap(); assert_event_ref_fields(&comment.root, &root); assert_event_ref_fields(&comment.parent, &root); @@ -106,15 +109,15 @@ fn comment_from_tags_defaults_parent_to_root() { #[test] fn comment_roundtrip_from_legacy_tags() { - let root = common::event_ref("root", "author", 1); - let parent = common::event_ref("parent", "author", 1); + let root = common::event_ref("root", "author", KIND_POST); + let parent = common::event_ref("parent", "author", KIND_POST); let tags = vec![ build_event_ref_tag(TAG_E_ROOT, &root), build_event_ref_tag(TAG_E_PREV, &parent), ]; - let comment = comment_from_tags(1, &tags, "hello").unwrap(); + let comment = comment_from_tags(KIND_COMMENT, &tags, "hello").unwrap(); assert_event_ref_fields(&comment.root, &root); assert_event_ref_fields(&comment.parent, &parent); @@ -124,16 +127,19 @@ fn comment_roundtrip_from_legacy_tags() { fn comment_from_tags_requires_root_tag() { let tags = vec![vec!["p".to_string(), "x".to_string()]]; - let err = comment_from_tags(1, &tags, "hello").unwrap_err(); + let err = comment_from_tags(KIND_COMMENT, &tags, "hello").unwrap_err(); assert!(matches!(err, EventParseError::MissingTag("E"))); } #[test] fn comment_from_tags_rejects_wrong_kind() { let tags = vec![vec!["e".to_string(), "x".to_string()]]; - let err = comment_from_tags(2, &tags, "hello").unwrap_err(); + let err = comment_from_tags(KIND_POST, &tags, "hello").unwrap_err(); assert!(matches!( err, - EventParseError::InvalidKind { expected: "1", got: 2 } + EventParseError::InvalidKind { + expected: "1111", + got: KIND_POST + } )); } diff --git a/events-codec/tests/event_ref.rs b/events-codec/tests/event_ref.rs @@ -1,5 +1,6 @@ mod common; +use radroots_events::kinds::KIND_POST; use radroots_events_codec::error::EventParseError; use radroots_events_codec::event_ref::{build_event_ref_tag, find_event_ref_tag, parse_event_ref_tag}; @@ -25,7 +26,7 @@ fn build_and_parse_roundtrip_with_d_tag_and_relays() { #[test] fn build_and_parse_roundtrip_without_d_tag_or_relays() { - let event = common::event_ref("id", "author", 1); + let event = common::event_ref("id", "author", KIND_POST); let tag = build_event_ref_tag("e", &event); assert_eq!(tag.len(), 5); @@ -45,7 +46,7 @@ fn parse_event_ref_tag_allows_relay_only_fifth_entry() { "e".to_string(), "id".to_string(), "author".to_string(), - "1".to_string(), + KIND_POST.to_string(), "wss://relay".to_string(), ]; @@ -69,7 +70,7 @@ fn parse_event_ref_tag_rejects_invalid_kind() { #[test] fn find_event_ref_tag_locates_first_match() { - let event = common::event_ref("id", "author", 1); + let event = common::event_ref("id", "author", KIND_POST); let tags = vec![ vec!["p".to_string(), "pubkey".to_string()], build_event_ref_tag("e", &event), diff --git a/events-codec/tests/follow.rs b/events-codec/tests/follow.rs @@ -1,4 +1,7 @@ -use radroots_events::follow::{RadrootsFollow, RadrootsFollowProfile}; +use radroots_events::{ + follow::{RadrootsFollow, RadrootsFollowProfile}, + kinds::{KIND_FOLLOW, KIND_POST}, +}; use radroots_events_codec::error::{EventEncodeError, EventParseError}; use radroots_events_codec::follow::decode::follow_from_tags; @@ -16,7 +19,7 @@ fn follow_to_wire_parts_builds_p_tags() { }; let parts = to_wire_parts(&follow).unwrap(); - assert_eq!(parts.kind, 3); + assert_eq!(parts.kind, KIND_FOLLOW); assert_eq!(parts.content, ""); assert_eq!(parts.tags.len(), 1); @@ -49,7 +52,7 @@ fn follow_to_wire_parts_requires_public_key() { fn follow_from_tags_defaults_published_at() { let tags = vec![vec!["p".to_string(), "pubkey".to_string()]]; - let follow = follow_from_tags(3, &tags, 123).unwrap(); + let follow = follow_from_tags(KIND_FOLLOW, &tags, 123).unwrap(); assert_eq!(follow.list.len(), 1); assert_eq!(follow.list[0].published_at, 123); assert_eq!(follow.list[0].public_key, "pubkey"); @@ -65,7 +68,7 @@ fn follow_from_tags_accepts_contact_without_relay() { "alice".to_string(), ]]; - let follow = follow_from_tags(3, &tags, 123).unwrap(); + let follow = follow_from_tags(KIND_FOLLOW, &tags, 123).unwrap(); assert_eq!(follow.list[0].published_at, 123); assert_eq!(follow.list[0].public_key, "pubkey"); assert!(follow.list[0].relay_url.is_none()); @@ -82,16 +85,19 @@ fn follow_from_tags_uses_tag_published_at() { "77".to_string(), ]]; - let follow = follow_from_tags(3, &tags, 123).unwrap(); + let follow = follow_from_tags(KIND_FOLLOW, &tags, 123).unwrap(); assert_eq!(follow.list[0].published_at, 77); } #[test] fn follow_from_tags_rejects_wrong_kind() { let tags = vec![vec!["p".to_string(), "pubkey".to_string()]]; - let err = follow_from_tags(4, &tags, 123).unwrap_err(); + let err = follow_from_tags(KIND_POST, &tags, 123).unwrap_err(); assert!(matches!( err, - EventParseError::InvalidKind { expected: "3", got: 4 } + EventParseError::InvalidKind { + expected: "3", + got: KIND_POST + } )); } diff --git a/events-codec/tests/job_feedback.rs b/events-codec/tests/job_feedback.rs @@ -2,7 +2,7 @@ mod common; use radroots_events::job::{JobFeedbackStatus, JobPaymentRequest}; use radroots_events::job_feedback::RadrootsJobFeedback; -use radroots_events::kinds::KIND_JOB_FEEDBACK; +use radroots_events::kinds::{KIND_JOB_FEEDBACK, KIND_JOB_RESULT_MIN, KIND_JOB_REQUEST_MIN}; use radroots_events_codec::job::encode::JobEncodeError; use radroots_events_codec::job::error::JobParseError; use radroots_events_codec::job::feedback::decode::job_feedback_from_tags; @@ -37,10 +37,13 @@ fn job_feedback_roundtrip_from_tags() { #[test] fn job_feedback_requires_valid_kind() { let mut fb = sample_feedback(); - fb.kind = 7001; + fb.kind = KIND_JOB_RESULT_MIN as u16; let err = to_wire_parts(&fb, "payload").unwrap_err(); - assert!(matches!(err, JobEncodeError::InvalidKind(7001))); + assert!(matches!( + err, + JobEncodeError::InvalidKind(KIND_JOB_RESULT_MIN) + )); } #[test] @@ -66,7 +69,7 @@ fn job_feedback_metadata_rejects_wrong_kind() { "id".to_string(), "author".to_string(), 1, - 1000, + KIND_JOB_REQUEST_MIN, "payload".to_string(), Vec::new(), ) diff --git a/events-codec/tests/job_request.rs b/events-codec/tests/job_request.rs @@ -1,4 +1,5 @@ use radroots_events::job::JobInputType; +use radroots_events::kinds::{KIND_JOB_FEEDBACK, KIND_JOB_REQUEST_MIN, KIND_JOB_RESULT_MIN}; use radroots_events::job_request::{RadrootsJobInput, RadrootsJobParam, RadrootsJobRequest}; use radroots_events_codec::job::encode::JobEncodeError; use radroots_events_codec::job::error::JobParseError; @@ -7,7 +8,7 @@ use radroots_events_codec::job::request::encode::to_wire_parts; fn sample_request() -> RadrootsJobRequest { RadrootsJobRequest { - kind: 5001, + kind: (KIND_JOB_REQUEST_MIN + 1) as u16, inputs: vec![RadrootsJobInput { data: "https://example.com".to_string(), input_type: JobInputType::Url, @@ -39,10 +40,13 @@ fn job_request_roundtrip_from_tags() { #[test] fn job_request_requires_valid_kind() { let mut req = sample_request(); - req.kind = 7000; + req.kind = KIND_JOB_FEEDBACK as u16; let err = to_wire_parts(&req, "payload").unwrap_err(); - assert!(matches!(err, JobEncodeError::InvalidKind(7000))); + assert!(matches!( + err, + JobEncodeError::InvalidKind(KIND_JOB_FEEDBACK) + )); } #[test] @@ -55,7 +59,7 @@ fn job_request_requires_providers_when_encrypted() { assert!(matches!(err, JobEncodeError::MissingProvidersForEncrypted)); let tags = vec![vec!["encrypted".to_string()]]; - let err = job_request_from_tags(5001, &tags).unwrap_err(); + let err = job_request_from_tags(KIND_JOB_REQUEST_MIN + 1, &tags).unwrap_err(); assert!(matches!(err, JobParseError::MissingTag("p"))); } @@ -65,7 +69,7 @@ fn job_request_metadata_rejects_wrong_kind() { "id".to_string(), "author".to_string(), 1, - 1000, + KIND_JOB_RESULT_MIN, Vec::new(), ) .unwrap_err(); diff --git a/events-codec/tests/job_result.rs b/events-codec/tests/job_result.rs @@ -1,6 +1,7 @@ mod common; use radroots_events::job::{JobInputType, JobPaymentRequest}; +use radroots_events::kinds::{KIND_JOB_REQUEST_MIN, KIND_JOB_RESULT_MIN}; use radroots_events::job_request::RadrootsJobInput; use radroots_events::job_result::RadrootsJobResult; use radroots_events_codec::job::encode::JobEncodeError; @@ -10,7 +11,7 @@ use radroots_events_codec::job::result::encode::to_wire_parts; fn sample_result() -> RadrootsJobResult { RadrootsJobResult { - kind: 6001, + kind: (KIND_JOB_RESULT_MIN + 1) as u16, request_event: common::event_ptr("req", Some("wss://relay")), request_json: Some("{\"foo\":\"bar\"}".to_string()), inputs: vec![RadrootsJobInput { @@ -42,16 +43,19 @@ fn job_result_roundtrip_from_tags() { #[test] fn job_result_requires_valid_kind() { let mut res = sample_result(); - res.kind = 5000; + res.kind = KIND_JOB_REQUEST_MIN as u16; let err = to_wire_parts(&res, "payload").unwrap_err(); - assert!(matches!(err, JobEncodeError::InvalidKind(5000))); + assert!(matches!( + err, + JobEncodeError::InvalidKind(KIND_JOB_REQUEST_MIN) + )); } #[test] fn job_result_requires_request_event_tag() { let tags = vec![vec!["p".to_string(), "customer".to_string()]]; - let err = job_result_from_tags(6001, &tags, "payload").unwrap_err(); + let err = job_result_from_tags(KIND_JOB_RESULT_MIN + 1, &tags, "payload").unwrap_err(); assert!(matches!(err, JobParseError::MissingTag("e"))); } @@ -61,7 +65,7 @@ fn job_result_metadata_rejects_wrong_kind() { "id".to_string(), "author".to_string(), 1, - 1000, + KIND_JOB_REQUEST_MIN, "payload".to_string(), Vec::new(), ) diff --git a/events-codec/tests/job_traits.rs b/events-codec/tests/job_traits.rs @@ -1,12 +1,13 @@ use radroots_events::job::JobInputType; use radroots_events::job_request::{RadrootsJobInput, RadrootsJobParam, RadrootsJobRequest}; +use radroots_events::kinds::KIND_JOB_REQUEST_MIN; use radroots_events::RadrootsNostrEvent; use radroots_events_codec::job::request::encode::to_wire_parts; use radroots_events_codec::job::traits::{BorrowedEventAdapter, JobEventLike}; fn sample_request() -> RadrootsJobRequest { RadrootsJobRequest { - kind: 5001, + kind: (KIND_JOB_REQUEST_MIN + 1) as u16, inputs: vec![RadrootsJobInput { data: "hello".to_string(), input_type: JobInputType::Text, diff --git a/events-codec/tests/listing.rs b/events-codec/tests/listing.rs @@ -4,11 +4,14 @@ use radroots_core::{ RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreQuantity, RadrootsCoreQuantityPrice, RadrootsCoreUnit, }; -use radroots_events::listing::{ - RadrootsListing, RadrootsListingAvailability, RadrootsListingDeliveryMethod, - RadrootsListingDiscount, RadrootsListingImage, RadrootsListingImageSize, - RadrootsListingLocation, RadrootsListingProduct, RadrootsListingQuantity, - RadrootsListingStatus, +use radroots_events::{ + kinds::{KIND_LISTING, KIND_POST}, + listing::{ + RadrootsListing, RadrootsListingAvailability, RadrootsListingDeliveryMethod, + RadrootsListingDiscount, RadrootsListingImage, RadrootsListingImageSize, + RadrootsListingLocation, RadrootsListingProduct, RadrootsListingQuantity, + RadrootsListingStatus, + }, }; use radroots_events::tags::TAG_D; use radroots_events_codec::error::{EventEncodeError, EventParseError}; @@ -137,7 +140,7 @@ fn listing_from_event_fills_missing_d_tag() { let content = serde_json::to_string(&listing).unwrap(); let tags = vec![vec![TAG_D.to_string(), "filled".to_string()]]; - let decoded = listing_from_event(30402, &tags, &content).unwrap(); + let decoded = listing_from_event(KIND_LISTING, &tags, &content).unwrap(); assert_eq!(decoded.d_tag, "filled"); } @@ -147,7 +150,7 @@ fn listing_from_event_rejects_mismatched_d_tag() { let content = serde_json::to_string(&listing).unwrap(); let tags = vec![vec![TAG_D.to_string(), "b".to_string()]]; - let err = listing_from_event(30402, &tags, &content).unwrap_err(); + let err = listing_from_event(KIND_LISTING, &tags, &content).unwrap_err(); assert!(matches!(err, EventParseError::InvalidTag(TAG_D))); } @@ -157,12 +160,12 @@ fn listing_from_event_rejects_wrong_kind() { let content = serde_json::to_string(&listing).unwrap(); let tags = vec![vec![TAG_D.to_string(), "listing-1".to_string()]]; - let err = listing_from_event(1, &tags, &content).unwrap_err(); + let err = listing_from_event(KIND_POST, &tags, &content).unwrap_err(); assert!(matches!( err, EventParseError::InvalidKind { expected: "30402", - got: 1 + got: KIND_POST } )); } diff --git a/events-codec/tests/message.rs b/events-codec/tests/message.rs @@ -1,5 +1,6 @@ use radroots_events::{ RadrootsNostrEventPtr, + kinds::{KIND_MESSAGE, KIND_POST}, message::{RadrootsMessage, RadrootsMessageRecipient}, }; use radroots_events_codec::error::{EventEncodeError, EventParseError}; @@ -82,7 +83,7 @@ fn message_to_wire_parts_sets_tags() { }; let parts = to_wire_parts(&message).unwrap(); - assert_eq!(parts.kind, 14); + assert_eq!(parts.kind, KIND_MESSAGE); assert_eq!(parts.content, "hello"); assert_eq!( parts.tags, @@ -106,16 +107,19 @@ fn message_to_wire_parts_sets_tags() { #[test] fn message_from_tags_requires_kind_content_and_recipients() { let tags = vec![vec!["p".to_string(), "pub".to_string()]]; - let err = message_from_tags(1, &tags, "hello").unwrap_err(); + let err = message_from_tags(KIND_POST, &tags, "hello").unwrap_err(); assert!(matches!( err, - EventParseError::InvalidKind { expected: "14", got: 1 } + EventParseError::InvalidKind { + expected: "14", + got: KIND_POST + } )); - let err = message_from_tags(14, &tags, " ").unwrap_err(); + let err = message_from_tags(KIND_MESSAGE, &tags, " ").unwrap_err(); assert!(matches!(err, EventParseError::InvalidTag("content"))); - let err = message_from_tags(14, &[], "hello").unwrap_err(); + let err = message_from_tags(KIND_MESSAGE, &[], "hello").unwrap_err(); assert!(matches!(err, EventParseError::MissingTag("p"))); } @@ -136,7 +140,7 @@ fn message_roundtrip_from_tags() { vec!["subject".to_string(), "topic".to_string()], ]; - let message = message_from_tags(14, &tags, "hello").unwrap(); + let message = message_from_tags(KIND_MESSAGE, &tags, "hello").unwrap(); assert_eq!(message.recipients.len(), 2); assert_eq!(message.recipients[0].public_key, "pub1"); diff --git a/events-codec/tests/post.rs b/events-codec/tests/post.rs @@ -1,4 +1,4 @@ -use radroots_events::post::RadrootsPost; +use radroots_events::{kinds::{KIND_COMMENT, KIND_POST}, post::RadrootsPost}; use radroots_events_codec::error::{EventEncodeError, EventParseError}; use radroots_events_codec::post::decode::post_from_content; use radroots_events_codec::post::encode::to_wire_parts; @@ -23,19 +23,22 @@ fn post_to_wire_parts_sets_kind_and_content() { }; let parts = to_wire_parts(&post).unwrap(); - assert_eq!(parts.kind, 1); + assert_eq!(parts.kind, KIND_POST); assert_eq!(parts.content, "hello"); assert!(parts.tags.is_empty()); } #[test] fn post_from_content_requires_kind_and_content() { - let err = post_from_content(2, "hello").unwrap_err(); + let err = post_from_content(KIND_COMMENT, "hello").unwrap_err(); assert!(matches!( err, - EventParseError::InvalidKind { expected: "1", got: 2 } + EventParseError::InvalidKind { + expected: "1", + got: KIND_COMMENT + } )); - let err = post_from_content(1, " ").unwrap_err(); + let err = post_from_content(KIND_POST, " ").unwrap_err(); assert!(matches!(err, EventParseError::InvalidTag("content"))); } diff --git a/events-codec/tests/profile.rs b/events-codec/tests/profile.rs @@ -1,5 +1,6 @@ #![cfg(feature = "serde_json")] +use radroots_events::kinds::KIND_POST; use radroots_events_codec::error::EventParseError; use radroots_events_codec::profile::decode::profile_from_content; @@ -48,6 +49,9 @@ fn profile_metadata_rejects_wrong_kind() { assert!(matches!( err, - EventParseError::InvalidKind { expected: "0", got: 1 } + EventParseError::InvalidKind { + expected: "0", + got: KIND_POST + } )); } diff --git a/events-codec/tests/profile_encode.rs b/events-codec/tests/profile_encode.rs @@ -1,6 +1,6 @@ #![cfg(all(feature = "nostr", feature = "serde_json"))] -use radroots_events::profile::RadrootsProfile; +use radroots_events::{kinds::KIND_PROFILE, profile::RadrootsProfile}; use radroots_events_codec::profile::encode::{to_metadata, to_wire_parts}; use radroots_events_codec::profile::error::ProfileEncodeError; use serde_json::Value; @@ -43,7 +43,7 @@ fn profile_to_wire_parts_writes_json_content() { }; let parts = to_wire_parts(&profile).unwrap(); - assert_eq!(parts.kind, 0); + assert_eq!(parts.kind, KIND_PROFILE); let value: Value = serde_json::from_str(&parts.content).unwrap(); assert_eq!(value.get("name").and_then(|v| v.as_str()), Some("alice")); diff --git a/events-codec/tests/reaction.rs b/events-codec/tests/reaction.rs @@ -1,6 +1,9 @@ mod common; -use radroots_events::reaction::RadrootsReaction; +use radroots_events::{ + kinds::{KIND_POST, KIND_REACTION}, + reaction::RadrootsReaction, +}; use radroots_events::tags::TAG_E_ROOT; use radroots_events_codec::error::{EventEncodeError, EventParseError}; @@ -11,7 +14,7 @@ use radroots_events_codec::reaction::encode::{reaction_build_tags, to_wire_parts #[test] fn reaction_build_tags_requires_root_fields() { let reaction = RadrootsReaction { - root: common::event_ref("", "author", 1), + root: common::event_ref("", "author", KIND_POST), content: "like".to_string(), }; @@ -25,7 +28,7 @@ fn reaction_build_tags_requires_root_fields() { #[test] fn reaction_to_wire_parts_requires_content() { let reaction = RadrootsReaction { - root: common::event_ref("root", "author", 1), + root: common::event_ref("root", "author", KIND_POST), content: " ".to_string(), }; @@ -39,7 +42,7 @@ fn reaction_to_wire_parts_requires_content() { #[test] fn reaction_from_tags_requires_root_tag() { let tags = vec![vec!["p".to_string(), "x".to_string()]]; - let err = reaction_from_tags(7, &tags, "+").unwrap_err(); + let err = reaction_from_tags(KIND_REACTION, &tags, "+").unwrap_err(); assert!(matches!(err, EventParseError::MissingTag("e"))); } @@ -48,14 +51,14 @@ fn reaction_roundtrip_from_tags() { let root = common::event_ref_with_d( "root", "author", - 1, + KIND_POST, "note-1", Some(vec!["wss://relay".to_string()]), ); let mut tags = Vec::new(); push_nip10_ref_tags(&mut tags, &root, "e", "p", "k", "a"); - let reaction = reaction_from_tags(7, &tags, "+").unwrap(); + let reaction = reaction_from_tags(KIND_REACTION, &tags, "+").unwrap(); assert_eq!(reaction.root.id, root.id); assert_eq!(reaction.root.author, root.author); @@ -67,10 +70,10 @@ fn reaction_roundtrip_from_tags() { #[test] fn reaction_roundtrip_from_legacy_tags() { - let root = common::event_ref("root", "author", 1); + let root = common::event_ref("root", "author", KIND_POST); let tags = vec![build_event_ref_tag(TAG_E_ROOT, &root)]; - let reaction = reaction_from_tags(7, &tags, "+").unwrap(); + let reaction = reaction_from_tags(KIND_REACTION, &tags, "+").unwrap(); assert_eq!(reaction.root.id, root.id); assert_eq!(reaction.root.author, root.author); diff --git a/events-codec/tests/wire.rs b/events-codec/tests/wire.rs @@ -1,3 +1,4 @@ +use radroots_events::kinds::KIND_POST; use radroots_events_codec::wire::{canonicalize_tags, empty_content, to_draft, WireEventParts}; #[test] @@ -24,14 +25,14 @@ fn canonicalize_tags_trims_sorts_and_dedups() { #[test] fn to_draft_copies_fields() { let parts = WireEventParts { - kind: 42, + kind: KIND_POST, content: "hello".to_string(), tags: vec![vec!["t".to_string(), "a".to_string()]], }; let draft = to_draft(parts, "author", 99); - assert_eq!(draft.kind, 42); + assert_eq!(draft.kind, KIND_POST); assert_eq!(draft.created_at, 99); assert_eq!(draft.author, "author"); assert_eq!(draft.content, "hello"); diff --git a/events/bindings/ts/src/typeshare-types.ts b/events/bindings/ts/src/typeshare-types.ts @@ -2,6 +2,14 @@ Generated by typeshare 1.13.3 */ +export const KIND_PROFILE: number = 0; +export const KIND_POST: number = 1; +export const KIND_FOLLOW: number = 3; +export const KIND_REACTION: number = 7; +export const KIND_MESSAGE: number = 14; +export const KIND_COMMENT: number = 1111; +export const KIND_APP_DATA: number = 30078; +export const KIND_LISTING: number = 30402; export const KIND_APPLICATION_HANDLER: number = 31990; export const KIND_JOB_REQUEST_MIN: number = 5000; export const KIND_JOB_REQUEST_MAX: number = 5999; diff --git a/events/src/app_data.rs b/events/src/app_data.rs @@ -1,13 +1,13 @@ #![forbid(unsafe_code)] -use crate::RadrootsNostrEvent; +use crate::{RadrootsNostrEvent, kinds::KIND_APP_DATA as KIND_APP_DATA_EVENT}; #[cfg(feature = "ts-rs")] use ts_rs::TS; #[cfg(not(feature = "std"))] use alloc::string::String; -pub const KIND_APP_DATA: u32 = 30078; +pub const KIND_APP_DATA: u32 = KIND_APP_DATA_EVENT; #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] diff --git a/events/src/kinds.rs b/events/src/kinds.rs @@ -1,4 +1,20 @@ #[cfg_attr(feature = "typeshare", typeshare::typeshare)] +pub const KIND_PROFILE: u32 = 0; +#[cfg_attr(feature = "typeshare", typeshare::typeshare)] +pub const KIND_POST: u32 = 1; +#[cfg_attr(feature = "typeshare", typeshare::typeshare)] +pub const KIND_FOLLOW: u32 = 3; +#[cfg_attr(feature = "typeshare", typeshare::typeshare)] +pub const KIND_REACTION: u32 = 7; +#[cfg_attr(feature = "typeshare", typeshare::typeshare)] +pub const KIND_MESSAGE: u32 = 14; +#[cfg_attr(feature = "typeshare", typeshare::typeshare)] +pub const KIND_COMMENT: u32 = 1111; +#[cfg_attr(feature = "typeshare", typeshare::typeshare)] +pub const KIND_APP_DATA: u32 = 30078; +#[cfg_attr(feature = "typeshare", typeshare::typeshare)] +pub const KIND_LISTING: u32 = 30402; +#[cfg_attr(feature = "typeshare", typeshare::typeshare)] pub const KIND_APPLICATION_HANDLER: u32 = 31990; #[cfg_attr(feature = "typeshare", typeshare::typeshare)] diff --git a/events/src/typeshare_kinds.rs b/events/src/typeshare_kinds.rs @@ -1,4 +1,20 @@ #[typeshare::typeshare] +pub const KIND_PROFILE: u32 = 0; +#[typeshare::typeshare] +pub const KIND_POST: u32 = 1; +#[typeshare::typeshare] +pub const KIND_FOLLOW: u32 = 3; +#[typeshare::typeshare] +pub const KIND_REACTION: u32 = 7; +#[typeshare::typeshare] +pub const KIND_MESSAGE: u32 = 14; +#[typeshare::typeshare] +pub const KIND_COMMENT: u32 = 1111; +#[typeshare::typeshare] +pub const KIND_APP_DATA: u32 = 30078; +#[typeshare::typeshare] +pub const KIND_LISTING: u32 = 30402; +#[typeshare::typeshare] pub const KIND_APPLICATION_HANDLER: u32 = 31990; #[typeshare::typeshare] diff --git a/nostr/src/job_adapter.rs b/nostr/src/job_adapter.rs @@ -1,5 +1,6 @@ #![forbid(unsafe_code)] +use radroots_events::kinds::KIND_PROFILE; use radroots_events_codec::job::traits::{JobEventBorrow, JobEventLike}; use crate::types::{RadrootsNostrEvent, RadrootsNostrKind}; @@ -47,7 +48,7 @@ impl<'a> JobEventBorrow<'a> for RadrootsNostrEventAdapter<'a> { fn raw_kind(&'a self) -> u32 { match self.evt.kind { RadrootsNostrKind::Custom(v) => v as u32, - _ => 0, + _ => KIND_PROFILE, } } } @@ -65,7 +66,7 @@ impl JobEventLike for RadrootsNostrEventAdapter<'_> { fn raw_kind(&self) -> u32 { match self.evt.kind { RadrootsNostrKind::Custom(v) => v as u32, - _ => 0, + _ => KIND_PROFILE, } } fn raw_content(&self) -> String { diff --git a/trade/src/listing/dvm.rs b/trade/src/listing/dvm.rs @@ -3,7 +3,7 @@ #[cfg(not(feature = "std"))] use alloc::{string::String, vec::Vec}; -use radroots_events::RadrootsNostrEventPtr; +use radroots_events::{RadrootsNostrEventPtr, kinds::KIND_PROFILE}; #[cfg(feature = "ts-rs")] use ts_rs::TS; @@ -220,7 +220,10 @@ impl TradeListingAddress { if parts.next().is_some() { return Err(TradeListingAddressError::InvalidFormat); } - if kind == 0 || seller_pubkey.trim().is_empty() || listing_id.trim().is_empty() { + if kind == KIND_PROFILE as u16 + || seller_pubkey.trim().is_empty() + || listing_id.trim().is_empty() + { return Err(TradeListingAddressError::InvalidFormat); } Ok(Self { @@ -332,6 +335,7 @@ mod tests { TradeListingEnvelope, TradeListingEnvelopeError, TradeListingMessageType, TradeListingValidateRequest, }; + use radroots_events::kinds::KIND_LISTING; #[test] fn envelope_requires_listing_addr() { @@ -351,7 +355,7 @@ mod tests { fn envelope_requires_order_id_for_order_scoped() { let env = TradeListingEnvelope::new( TradeListingMessageType::OrderRequest, - "30402:pubkey:listing", + format!("{KIND_LISTING}:pubkey:listing"), None, TradeListingValidateRequest { listing_event: None }, ); diff --git a/trade/src/listing/tags.rs b/trade/src/listing/tags.rs @@ -102,6 +102,7 @@ pub fn validate_trade_listing_chain(tags: &[Vec<String>]) -> Result<(), JobParse #[cfg(test)] mod tests { use super::{trade_listing_dvm_tags, validate_trade_listing_chain}; + use radroots_events::kinds::KIND_LISTING; use radroots_events::tags::{TAG_D, TAG_E_ROOT}; use radroots_events_codec::job::error::JobParseError; @@ -141,10 +142,11 @@ mod tests { #[test] fn trade_listing_dvm_tags_builds_expected_tags() { - let tags = trade_listing_dvm_tags("pubkey", "30402:pubkey:listing", Some("order-1")); + let listing_addr = format!("{KIND_LISTING}:pubkey:listing"); + let tags = trade_listing_dvm_tags("pubkey", &listing_addr, Some("order-1")); let expected: Vec<Vec<String>> = vec![ vec![String::from("p"), String::from("pubkey")], - vec![String::from("a"), String::from("30402:pubkey:listing")], + vec![String::from("a"), listing_addr.clone()], vec![String::from(TAG_D), String::from("order-1")], ]; assert_eq!(tags, expected); @@ -152,10 +154,11 @@ mod tests { #[test] fn trade_listing_dvm_tags_omit_order_id_when_missing() { - let tags = trade_listing_dvm_tags("pubkey", "30402:pubkey:listing", None::<String>); + let listing_addr = format!("{KIND_LISTING}:pubkey:listing"); + let tags = trade_listing_dvm_tags("pubkey", &listing_addr, None::<String>); let expected: Vec<Vec<String>> = vec![ vec![String::from("p"), String::from("pubkey")], - vec![String::from("a"), String::from("30402:pubkey:listing")], + vec![String::from("a"), listing_addr.clone()], ]; assert_eq!(tags, expected); } diff --git a/trade/src/listing/validation.rs b/trade/src/listing/validation.rs @@ -6,6 +6,7 @@ use alloc::{string::String, vec::Vec}; use radroots_core::{RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreUnit}; use radroots_events::{ RadrootsNostrEvent, + kinds::KIND_LISTING, listing::{ RadrootsListing, RadrootsListingAvailability, RadrootsListingDeliveryMethod, RadrootsListingLocation, @@ -17,8 +18,6 @@ use ts_rs::TS; use crate::listing::codec::{TradeListingParseError, listing_from_event_parts}; use crate::listing::dvm::TradeListingAddress; -const LISTING_KIND: u32 = 30402; - #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -116,7 +115,7 @@ impl std::error::Error for TradeListingValidationError {} pub fn validate_listing_event( event: &RadrootsNostrEvent, ) -> Result<RadrootsTradeListing, TradeListingValidationError> { - if event.kind != LISTING_KIND { + if event.kind != KIND_LISTING { return Err(TradeListingValidationError::InvalidKind { kind: event.kind }); } @@ -129,7 +128,7 @@ pub fn validate_listing_event( let seller_pubkey = event.author.clone(); let listing_addr = TradeListingAddress { - kind: LISTING_KIND as u16, + kind: KIND_LISTING as u16, seller_pubkey: seller_pubkey.clone(), listing_id: listing_id.clone(), } @@ -222,6 +221,7 @@ mod tests { }; use radroots_events::{ RadrootsNostrEvent, + kinds::KIND_LISTING, listing::{ RadrootsListing, RadrootsListingAvailability, RadrootsListingDeliveryMethod, RadrootsListingLocation, RadrootsListingProduct, RadrootsListingQuantity, @@ -284,7 +284,7 @@ mod tests { id: "evt".into(), author: "seller".into(), created_at: 0, - kind: 30402, + kind: KIND_LISTING, tags: vec![vec!["d".into(), listing.d_tag.clone()]], content: serde_json::to_string(listing).unwrap(), sig: "sig".into(),