lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

mod.rs (4082B)


      1 //! Typed Nostr event authoring profiles used by the Radroots adapter.
      2 //!
      3 //! Focused builders seal reserved kinds and canonical tag construction before
      4 //! signer access. Live publication, persistence, and retry policy remain
      5 //! outside this module.
      6 
      7 #[cfg(feature = "events")]
      8 pub mod application_handler;
      9 #[cfg(feature = "events")]
     10 pub mod calendar;
     11 #[cfg(feature = "events")]
     12 pub mod comment;
     13 #[cfg(feature = "events")]
     14 pub mod deletion;
     15 #[cfg(feature = "events")]
     16 pub mod food_availability;
     17 pub mod jobs;
     18 pub mod metadata;
     19 pub mod post;
     20 #[cfg(feature = "events")]
     21 pub mod reply;
     22 #[cfg(feature = "events")]
     23 mod sealed;
     24 
     25 extern crate alloc;
     26 #[cfg(any(feature = "events", test))]
     27 use alloc::{string::String, vec::Vec};
     28 
     29 #[cfg(any(feature = "events", test))]
     30 use crate::error::Error;
     31 #[cfg(any(feature = "events", test))]
     32 use crate::types::{
     33     RadrootsNostrEventBuilderUnchecked, RadrootsNostrKind, RadrootsNostrTag, RadrootsNostrTagKind,
     34 };
     35 
     36 #[cfg(any(feature = "events", test))]
     37 pub(crate) fn build_event_unchecked(
     38     kind_u32: u32,
     39     content: impl Into<String>,
     40     tag_slices: Vec<Vec<String>>,
     41 ) -> Result<RadrootsNostrEventBuilderUnchecked, Error> {
     42     let kind = u16::try_from(kind_u32).map_err(|_| Error::KindOutOfRange {
     43         kind: kind_u32,
     44         max: u16::MAX,
     45     })?;
     46     let mut tags: Vec<RadrootsNostrTag> = Vec::new();
     47     for mut s in tag_slices {
     48         if s.is_empty() {
     49             continue;
     50         }
     51         let key = s.remove(0);
     52         let values = s;
     53         tags.push(RadrootsNostrTag::custom(
     54             RadrootsNostrTagKind::Custom(key.into()),
     55             values,
     56         ));
     57     }
     58     let builder =
     59         RadrootsNostrEventBuilderUnchecked::new(RadrootsNostrKind::Custom(kind), content.into())
     60             .tags(tags)
     61             .allow_self_tagging();
     62     Ok(builder)
     63 }
     64 
     65 #[cfg(test)]
     66 mod tests {
     67     use alloc::{vec, vec::Vec};
     68 
     69     use super::build_event_unchecked;
     70     use crate::error::Error;
     71     use crate::test_fixtures::FIXTURE_ALICE_PUBLIC_KEY_HEX;
     72     use crate::types::{RadrootsNostrPublicKey, RadrootsNostrTagKind};
     73 
     74     #[test]
     75     fn build_event_preserves_self_p_tag() {
     76         let pubkey_hex = FIXTURE_ALICE_PUBLIC_KEY_HEX;
     77         let pubkey = RadrootsNostrPublicKey::from_hex(pubkey_hex).expect("pubkey");
     78         let tags = vec![
     79             vec!["x".to_string(), "v".to_string()],
     80             vec!["p".to_string(), pubkey_hex.to_string()],
     81         ];
     82 
     83         let builder = build_event_unchecked(1, "test", tags).expect("builder");
     84         let event = builder.build(pubkey);
     85 
     86         let has_self_tag = event.tags.iter().any(|tag| {
     87             tag.kind() == RadrootsNostrTagKind::p() && tag.content() == Some(pubkey_hex)
     88         });
     89         assert!(has_self_tag);
     90         let has_other_self_tag = event.tags.iter().any(|tag| {
     91             tag.kind() == RadrootsNostrTagKind::p()
     92                 && tag.content()
     93                     == Some("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
     94         });
     95         assert!(!has_other_self_tag);
     96     }
     97 
     98     #[test]
     99     fn build_event_accepts_maximum_nip01_kind() {
    100         let builder = build_event_unchecked(u32::from(u16::MAX), "test", Vec::new())
    101             .expect("maximum NIP-01 kind");
    102         let event = builder
    103             .build(RadrootsNostrPublicKey::from_hex(FIXTURE_ALICE_PUBLIC_KEY_HEX).expect("pubkey"));
    104         assert_eq!(event.kind.as_u16(), u16::MAX);
    105     }
    106 
    107     #[test]
    108     fn build_event_rejects_kind_overflow() {
    109         let kind = u32::from(u16::MAX) + 1;
    110         assert!(matches!(
    111             build_event_unchecked(kind, "test", Vec::new()),
    112             Err(Error::KindOutOfRange {
    113                 kind: actual,
    114                 max: u16::MAX
    115             }) if actual == kind
    116         ));
    117     }
    118 
    119     #[test]
    120     fn build_event_ignores_empty_tag_slices() {
    121         let builder = build_event_unchecked(1, "test", vec![Vec::new()]).expect("builder");
    122         let event = builder
    123             .build(RadrootsNostrPublicKey::from_hex(FIXTURE_ALICE_PUBLIC_KEY_HEX).expect("pubkey"));
    124         assert!(event.tags.is_empty());
    125     }
    126 }