lib

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

commit 1584da2bf081673ed1d446e7d4fc293c52e7a504
parent 5d13798e131a4833690f7b9c3bfafd95a202d519
Author: triesap <tyson@radroots.org>
Date:   Wed,  4 Mar 2026 14:11:02 +0000

nostr: migrate consumers to parsed event wrappers

Diffstat:
MCargo.lock | 1+
Mcrates/app-core/src/runtime/mod.rs | 9+++++++--
Mcrates/app-core/src/runtime/nostr.rs | 24++++++++++++------------
Mcrates/events-codec/src/profile/decode.rs | 8+-------
Mcrates/events-codec/src/profile/mod.rs | 9+++++++++
Mcrates/net-core/Cargo.toml | 4+++-
Mcrates/net-core/src/nostr_client/events/post.rs | 7++++---
Mcrates/net-core/src/nostr_client/events/profile.rs | 7++++---
Mcrates/net-core/src/nostr_client/inner.rs | 5+++--
Mcrates/net-core/src/nostr_client/manager.rs | 4+++-
Mcrates/nostr/Cargo.toml | 8+++++++-
Mcrates/nostr/src/codec_adapters.rs | 29+++++++++++++++++------------
Mcrates/nostr/src/event_adapters.rs | 64+++++++++++++++++++++++++++++++++++++---------------------------
Mcrates/nostr/src/events/post.rs | 5++++-
Mcrates/nostr/src/nip17.rs | 21+++++++++++----------
15 files changed, 123 insertions(+), 82 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -2729,6 +2729,7 @@ dependencies = [ "futures", "hex", "radroots-events", + "radroots-events-codec", "radroots-log", "radroots-nostr", "radroots-nostr-accounts", diff --git a/crates/app-core/src/runtime/mod.rs b/crates/app-core/src/runtime/mod.rs @@ -31,8 +31,13 @@ pub struct RadrootsRuntime { pub(crate) shutting_down: AtomicBool, pub(crate) platform_app: RwLock<Option<AppInfoPlatform>>, #[cfg(feature = "nostr-client")] - pub(crate) post_events_rx: - Mutex<Option<Receiver<radroots_events::post::RadrootsPostEventMetadata>>>, + pub(crate) post_events_rx: Mutex< + Option< + Receiver< + radroots_events_codec::parsed::RadrootsParsedData<radroots_events::post::RadrootsPost>, + >, + >, + >, } #[cfg_attr(not(coverage_nightly), uniffi::export)] diff --git a/crates/app-core/src/runtime/nostr.rs b/crates/app-core/src/runtime/nostr.rs @@ -64,14 +64,14 @@ pub struct NostrPostEventMetadata { #[cfg(feature = "nostr-client")] fn map_post_event_metadata( - event: radroots_events::post::RadrootsPostEventMetadata, + event: radroots_events_codec::parsed::RadrootsParsedData<radroots_events::post::RadrootsPost>, ) -> NostrPostEventMetadata { NostrPostEventMetadata { id: event.id, author: event.author, published_at: event.published_at as u64, post: NostrPost { - content: event.post.content, + content: event.data.content, }, } } @@ -164,16 +164,16 @@ impl RadrootsRuntime { author: m.author, published_at: m.published_at as u64, profile: NostrProfile { - name: m.profile.name.into(), - display_name: m.profile.display_name.into(), - nip05: m.profile.nip05.into(), - about: m.profile.about.into(), - website: m.profile.website, - picture: m.profile.picture, - banner: m.profile.banner, - lud06: m.profile.lud06, - lud16: m.profile.lud16, - bot: m.profile.bot, + name: m.data.profile.name.into(), + display_name: m.data.profile.display_name.into(), + nip05: m.data.profile.nip05.into(), + about: m.data.profile.about.into(), + website: m.data.profile.website, + picture: m.data.profile.picture, + banner: m.data.profile.banner, + lud06: m.data.profile.lud06, + lud16: m.data.profile.lud16, + bot: m.data.profile.bot, }, }); } diff --git a/crates/events-codec/src/profile/decode.rs b/crates/events-codec/src/profile/decode.rs @@ -6,6 +6,7 @@ use alloc::{ vec::Vec, }; +use super::RadrootsProfileData; use radroots_events::{ RadrootsNostrEvent, kinds::KIND_PROFILE, @@ -20,13 +21,6 @@ use serde_json::Value; const PROFILE_KIND: u32 = KIND_PROFILE; -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug)] -pub struct RadrootsProfileData { - pub profile_type: Option<RadrootsProfileType>, - pub profile: RadrootsProfile, -} - fn parse_optional_string(value: &Value, key: &'static str) -> Option<String> { value .get(key) diff --git a/crates/events-codec/src/profile/mod.rs b/crates/events-codec/src/profile/mod.rs @@ -1,6 +1,8 @@ #[cfg(not(feature = "std"))] extern crate alloc; +use radroots_events::profile::{RadrootsProfile, RadrootsProfileType}; + pub mod error; #[cfg(feature = "nostr")] @@ -8,3 +10,10 @@ pub mod encode; #[cfg(feature = "serde_json")] pub mod decode; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug)] +pub struct RadrootsProfileData { + pub profile_type: Option<RadrootsProfileType>, + pub profile: RadrootsProfile, +} diff --git a/crates/net-core/Cargo.toml b/crates/net-core/Cargo.toml @@ -18,6 +18,7 @@ rt = ["std", "dep:tokio"] nostr-client = [ "std", "dep:radroots-events", + "dep:radroots-events-codec", "radroots-events/serde", "dep:radroots-nostr-accounts", "dep:secrecy", @@ -33,7 +34,8 @@ fs-persistence = ["std"] radroots-events = { workspace = true, optional = true, default-features = true, features = ["std", "serde", "typeshare"] } radroots-log = { workspace = true, features = ["std"] } radroots-nostr-accounts = { workspace = true, optional = true, default-features = true } -radroots-nostr = { workspace = true, optional = true, default-features = true, features = ["client", "events"] } +radroots-events-codec = { workspace = true, optional = true, default-features = true, features = ["std"] } +radroots-nostr = { workspace = true, optional = true, default-features = true, features = ["client", "events", "codec"] } directories = { workspace = true, optional = true } hex = { workspace = true, optional = true } secrecy = { workspace = true, optional = true } diff --git a/crates/net-core/src/nostr_client/events/post.rs b/crates/net-core/src/nostr_client/events/post.rs @@ -1,5 +1,6 @@ use crate::error::{NetError, Result}; -use radroots_events::post::RadrootsPostEventMetadata; +use radroots_events::post::RadrootsPost; +use radroots_events_codec::parsed::RadrootsParsedData; use radroots_nostr::prelude::{ radroots_nostr_build_post_event, radroots_nostr_build_post_reply_event, radroots_nostr_fetch_post_events, radroots_nostr_send_event, @@ -68,7 +69,7 @@ impl NostrClientManager { &self, limit: u16, since_unix: Option<u64>, - ) -> Result<Vec<RadrootsPostEventMetadata>> { + ) -> Result<Vec<RadrootsParsedData<RadrootsPost>>> { let items = radroots_nostr_fetch_post_events(&self.inner.client, limit, since_unix) .await .map_err(|e| NetError::Msg(e.to_string()))?; @@ -79,7 +80,7 @@ impl NostrClientManager { &self, limit: u16, since_unix: Option<u64>, - ) -> Result<Vec<RadrootsPostEventMetadata>> { + ) -> Result<Vec<RadrootsParsedData<RadrootsPost>>> { let rt = self.inner.rt.clone(); let this = self.clone(); rt.block_on(async move { this.fetch_post_events(limit, since_unix).await }) diff --git a/crates/net-core/src/nostr_client/events/profile.rs b/crates/net-core/src/nostr_client/events/profile.rs @@ -1,5 +1,6 @@ use crate::error::{NetError, Result}; -use radroots_events::profile::RadrootsProfileEventMetadata; +use radroots_events_codec::parsed::RadrootsParsedData; +use radroots_events_codec::profile::RadrootsProfileData; use radroots_nostr::prelude::{ RadrootsNostrMetadata, RadrootsNostrPublicKey, radroots_nostr_fetch_metadata_for_author, radroots_nostr_post_metadata_event, @@ -11,7 +12,7 @@ impl NostrClientManager { pub async fn fetch_profile_event( &self, author: RadrootsNostrPublicKey, - ) -> Result<Option<RadrootsProfileEventMetadata>> { + ) -> Result<Option<RadrootsParsedData<RadrootsProfileData>>> { let ev = radroots_nostr_fetch_metadata_for_author( &self.inner.client, author, @@ -33,7 +34,7 @@ impl NostrClientManager { pub fn fetch_profile_event_blocking( &self, author: RadrootsNostrPublicKey, - ) -> Result<Option<RadrootsProfileEventMetadata>> { + ) -> Result<Option<RadrootsParsedData<RadrootsProfileData>>> { let rt = self.inner.rt.clone(); let this = self.clone(); rt.block_on(async move { this.fetch_profile_event(author).await }) diff --git a/crates/net-core/src/nostr_client/inner.rs b/crates/net-core/src/nostr_client/inner.rs @@ -1,7 +1,8 @@ use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use radroots_events::post::RadrootsPostEventMetadata; +use radroots_events::post::RadrootsPost; +use radroots_events_codec::parsed::RadrootsParsedData; use radroots_nostr::prelude::{ RadrootsNostrClient, RadrootsNostrKeys, RadrootsNostrMonitor, RadrootsNostrRelayStatus, RadrootsNostrRelayUrl, @@ -16,7 +17,7 @@ pub(super) struct Inner { pub statuses: Arc<Mutex<HashMap<RadrootsNostrRelayUrl, RadrootsNostrRelayStatus>>>, pub last_error: Arc<Mutex<Option<String>>>, pub rt: Handle, - pub post_events_tx: broadcast::Sender<RadrootsPostEventMetadata>, + pub post_events_tx: broadcast::Sender<RadrootsParsedData<RadrootsPost>>, pub post_events_stream: Arc<Mutex<Option<JoinHandle<()>>>>, } diff --git a/crates/net-core/src/nostr_client/manager.rs b/crates/net-core/src/nostr_client/manager.rs @@ -83,7 +83,9 @@ impl NostrClientManager { pub fn subscribe_post_events( &self, - ) -> tokio::sync::broadcast::Receiver<radroots_events::post::RadrootsPostEventMetadata> { + ) -> tokio::sync::broadcast::Receiver< + radroots_events_codec::parsed::RadrootsParsedData<radroots_events::post::RadrootsPost>, + > { self.inner.post_events_tx.subscribe() } } diff --git a/crates/nostr/Cargo.toml b/crates/nostr/Cargo.toml @@ -16,7 +16,13 @@ default = ["std"] std = [] client = ["std", "dep:nostr-sdk", "dep:radroots-identity"] codec = ["dep:radroots-events", "dep:radroots-events-codec", "radroots-events-codec/nostr"] -events = ["dep:radroots-events", "radroots-events/std", "radroots-events/serde"] +events = [ + "dep:radroots-events", + "dep:radroots-events-codec", + "radroots-events/std", + "radroots-events/serde", + "radroots-events-codec/std", +] http = ["dep:reqwest"] nip17 = ["std", "codec", "nostr/nip44", "nostr/nip59"] diff --git a/crates/nostr/src/codec_adapters.rs b/crates/nostr/src/codec_adapters.rs @@ -4,10 +4,15 @@ use alloc::{string::String, vec::Vec}; use crate::types::RadrootsNostrEvent; use crate::util::created_at_u32_saturating; +use radroots_events::{ + job_feedback::RadrootsJobFeedback, job_request::RadrootsJobRequest, + job_result::RadrootsJobResult, +}; use radroots_events_codec::job::{ error::JobParseError, feedback::decode as fb_decode, request::decode as req_decode, result::decode as res_decode, }; +use radroots_events_codec::parsed::{RadrootsParsedData, RadrootsParsedEvent}; fn event_id(e: &RadrootsNostrEvent) -> String { e.id.to_hex() @@ -39,8 +44,8 @@ fn sig_hex(e: &RadrootsNostrEvent) -> String { pub fn to_job_request_metadata( e: &RadrootsNostrEvent, -) -> Result<radroots_events::job_request::RadrootsJobRequestEventMetadata, JobParseError> { - req_decode::metadata_from_event( +) -> Result<RadrootsParsedData<RadrootsJobRequest>, JobParseError> { + req_decode::data_from_event( event_id(e), author(e), published_at(e), @@ -51,8 +56,8 @@ pub fn to_job_request_metadata( pub fn to_job_result_metadata( e: &RadrootsNostrEvent, -) -> Result<radroots_events::job_result::RadrootsJobResultEventMetadata, JobParseError> { - res_decode::metadata_from_event( +) -> Result<RadrootsParsedData<RadrootsJobResult>, JobParseError> { + res_decode::data_from_event( event_id(e), author(e), published_at(e), @@ -64,8 +69,8 @@ pub fn to_job_result_metadata( pub fn to_job_feedback_metadata( e: &RadrootsNostrEvent, -) -> Result<radroots_events::job_feedback::RadrootsJobFeedbackEventMetadata, JobParseError> { - fb_decode::metadata_from_event( +) -> Result<RadrootsParsedData<RadrootsJobFeedback>, JobParseError> { + fb_decode::data_from_event( event_id(e), author(e), published_at(e), @@ -77,8 +82,8 @@ pub fn to_job_feedback_metadata( pub fn to_job_request_index( e: &RadrootsNostrEvent, -) -> Result<radroots_events::job_request::RadrootsJobRequestEventIndex, JobParseError> { - req_decode::index_from_event( +) -> Result<RadrootsParsedEvent<RadrootsJobRequest>, JobParseError> { + req_decode::parsed_from_event( event_id(e), author(e), published_at(e), @@ -91,8 +96,8 @@ pub fn to_job_request_index( pub fn to_job_result_index( e: &RadrootsNostrEvent, -) -> Result<radroots_events::job_result::RadrootsJobResultEventIndex, JobParseError> { - res_decode::index_from_event( +) -> Result<RadrootsParsedEvent<RadrootsJobResult>, JobParseError> { + res_decode::parsed_from_event( event_id(e), author(e), published_at(e), @@ -105,8 +110,8 @@ pub fn to_job_result_index( pub fn to_job_feedback_index( e: &RadrootsNostrEvent, -) -> Result<radroots_events::job_feedback::RadrootsJobFeedbackEventIndex, JobParseError> { - fb_decode::index_from_event( +) -> Result<RadrootsParsedEvent<RadrootsJobFeedback>, JobParseError> { + fb_decode::parsed_from_event( event_id(e), author(e), published_at(e), diff --git a/crates/nostr/src/event_adapters.rs b/crates/nostr/src/event_adapters.rs @@ -1,10 +1,14 @@ #[cfg(feature = "events")] -use radroots_events::post::{RadrootsPost, RadrootsPostEventMetadata}; +use radroots_events::post::RadrootsPost; #[cfg(feature = "events")] use radroots_events::profile::{ - RADROOTS_PROFILE_TYPE_TAG_KEY, RadrootsProfile, RadrootsProfileEventMetadata, + RADROOTS_PROFILE_TYPE_TAG_KEY, RadrootsProfile, radroots_profile_type_from_tag_value, }; +#[cfg(feature = "events")] +use radroots_events_codec::parsed::RadrootsParsedData; +#[cfg(feature = "events")] +use radroots_events_codec::profile::RadrootsProfileData; #[cfg(feature = "events")] use crate::types::{RadrootsNostrEvent, RadrootsNostrMetadata}; @@ -13,20 +17,22 @@ use crate::types::{RadrootsNostrEvent, RadrootsNostrMetadata}; use crate::util::created_at_u32_saturating; #[cfg(feature = "events")] -pub fn to_post_event_metadata(e: &RadrootsNostrEvent) -> RadrootsPostEventMetadata { - RadrootsPostEventMetadata { - id: e.id.to_string(), - author: e.pubkey.to_string(), - published_at: created_at_u32_saturating(e.created_at), - kind: e.kind.as_u16() as u32, - post: RadrootsPost { +pub fn to_post_event_metadata(e: &RadrootsNostrEvent) -> RadrootsParsedData<RadrootsPost> { + RadrootsParsedData::new( + e.id.to_string(), + e.pubkey.to_string(), + created_at_u32_saturating(e.created_at), + e.kind.as_u16() as u32, + RadrootsPost { content: e.content.clone(), }, - } + ) } #[cfg(feature = "events")] -pub fn to_profile_event_metadata(e: &RadrootsNostrEvent) -> Option<RadrootsProfileEventMetadata> { +pub fn to_profile_event_metadata( + e: &RadrootsNostrEvent, +) -> Option<RadrootsParsedData<RadrootsProfileData>> { let profile_type = e .tags .iter() @@ -42,14 +48,16 @@ pub fn to_profile_event_metadata(e: &RadrootsNostrEvent) -> Option<RadrootsProfi .next(); if let Ok(p) = serde_json::from_str::<RadrootsProfile>(&e.content) { - return Some(RadrootsProfileEventMetadata { - id: e.id.to_string(), - author: e.pubkey.to_string(), - published_at: created_at_u32_saturating(e.created_at), - kind: e.kind.as_u16() as u32, - profile_type, - profile: p, - }); + return Some(RadrootsParsedData::new( + e.id.to_string(), + e.pubkey.to_string(), + created_at_u32_saturating(e.created_at), + e.kind.as_u16() as u32, + RadrootsProfileData { + profile_type, + profile: p, + }, + )); } if let Ok(md) = serde_json::from_str::<RadrootsNostrMetadata>(&e.content) { @@ -65,14 +73,16 @@ pub fn to_profile_event_metadata(e: &RadrootsNostrEvent) -> Option<RadrootsProfi lud16: md.lud16, bot: None, }; - return Some(RadrootsProfileEventMetadata { - id: e.id.to_string(), - author: e.pubkey.to_string(), - published_at: created_at_u32_saturating(e.created_at), - kind: e.kind.as_u16() as u32, - profile_type, - profile: p, - }); + return Some(RadrootsParsedData::new( + e.id.to_string(), + e.pubkey.to_string(), + created_at_u32_saturating(e.created_at), + e.kind.as_u16() as u32, + RadrootsProfileData { + profile_type, + profile: p, + }, + )); } None diff --git a/crates/nostr/src/events/post.rs b/crates/nostr/src/events/post.rs @@ -56,7 +56,10 @@ pub async fn radroots_nostr_fetch_post_events( client: &RadrootsNostrClient, limit: u16, since_unix: Option<u64>, -) -> Result<Vec<radroots_events::post::RadrootsPostEventMetadata>, RadrootsNostrError> { +) -> Result< + Vec<radroots_events_codec::parsed::RadrootsParsedData<radroots_events::post::RadrootsPost>>, + RadrootsNostrError, +> { let filter = radroots_nostr_post_events_filter(Some(limit), since_unix); let events = client.fetch_events(filter, Duration::from_secs(10)).await?; diff --git a/crates/nostr/src/nip17.rs b/crates/nostr/src/nip17.rs @@ -11,13 +11,14 @@ use nostr::{ use thiserror::Error; use radroots_events::kinds::{KIND_MESSAGE, KIND_MESSAGE_FILE}; -use radroots_events::message::{RadrootsMessage, RadrootsMessageEventMetadata}; -use radroots_events::message_file::{RadrootsMessageFile, RadrootsMessageFileEventMetadata}; +use radroots_events::message::RadrootsMessage; +use radroots_events::message_file::RadrootsMessageFile; use radroots_events_codec::error::{EventEncodeError, EventParseError}; use radroots_events_codec::message::decode as message_decode; use radroots_events_codec::message::encode as message_encode; use radroots_events_codec::message_file::decode as message_file_decode; use radroots_events_codec::message_file::encode as message_file_encode; +use radroots_events_codec::parsed::RadrootsParsedData; use radroots_events_codec::wire::WireEventParts; use crate::util::created_at_u32_saturating; @@ -42,8 +43,8 @@ pub enum RadrootsNip17Error { #[derive(Clone, Debug)] pub enum RadrootsNip17Rumor { - Message(RadrootsMessageEventMetadata), - MessageFile(RadrootsMessageFileEventMetadata), + Message(RadrootsParsedData<RadrootsMessage>), + MessageFile(RadrootsParsedData<RadrootsMessageFile>), } #[derive(Clone, Debug)] @@ -192,11 +193,11 @@ where match kind { KIND_MESSAGE => { let metadata = - message_decode::metadata_from_event(id, author, published_at, kind, content, tags)?; + message_decode::data_from_event(id, author, published_at, kind, content, tags)?; Ok(RadrootsNip17Rumor::Message(metadata)) } KIND_MESSAGE_FILE => { - let metadata = message_file_decode::metadata_from_event( + let metadata = message_file_decode::data_from_event( id, author, published_at, @@ -254,8 +255,8 @@ mod tests { .unwrap(); match rumor { RadrootsNip17Rumor::Message(metadata) => { - assert_eq!(metadata.message.content, "hello"); - assert_eq!(metadata.message.recipients.len(), 1); + assert_eq!(metadata.data.content, "hello"); + assert_eq!(metadata.data.recipients.len(), 1); } other => panic!("expected message rumor, got {other:?}"), } @@ -301,8 +302,8 @@ mod tests { .unwrap(); match rumor { RadrootsNip17Rumor::MessageFile(metadata) => { - assert_eq!(metadata.message_file.file_url, message.file_url); - assert_eq!(metadata.message_file.encrypted_hash, message.encrypted_hash); + assert_eq!(metadata.data.file_url, message.file_url); + assert_eq!(metadata.data.encrypted_hash, message.encrypted_hash); } other => panic!("expected message file rumor, got {other:?}"), }