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:
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:?}"),
}