commit 753b7016cb68e3903bf950ebce489fb731224d56
parent 82c5991a05dd6a37dc2342d1e081ec9fda9b2157
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Sat, 2 Aug 2025 19:42:07 +0000
Add `nostr-rs-relay` relay events records parser.
Diffstat:
7 files changed, 133 insertions(+), 56 deletions(-)
diff --git a/crates/indexer/src/domain/event/kind.rs b/crates/indexer/src/domain/event/kind.rs
@@ -5,27 +5,27 @@ use std::fmt;
use crate::domain::event::{IndexerKey, METADATA_INDEX_DIRECTORY};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum IndexerEvent {
+pub enum IndexerEventKind {
Metadata,
}
-impl IndexerEvent {
- pub const ALL: [IndexerEvent; 1] = [IndexerEvent::Metadata];
+impl IndexerEventKind {
+ pub const ALL: [IndexerEventKind; 1] = [IndexerEventKind::Metadata];
pub const fn as_u64(self) -> u64 {
match self {
- IndexerEvent::Metadata => 0,
+ IndexerEventKind::Metadata => 0,
}
}
pub const fn paths(self) -> &'static [IndexerKey] {
match self {
- IndexerEvent::Metadata => &METADATA_INDEX_DIRECTORY,
+ IndexerEventKind::Metadata => &METADATA_INDEX_DIRECTORY,
}
}
}
-impl fmt::Display for IndexerEvent {
+impl fmt::Display for IndexerEventKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_u64())
}
@@ -33,20 +33,20 @@ impl fmt::Display for IndexerEvent {
#[derive(thiserror::Error, Debug)]
#[error("unknown event kind: {0}")]
-pub struct IndexerEventParseError(pub u64);
+pub struct IndexerEventKindParseError(pub u64);
-impl TryFrom<u64> for IndexerEvent {
- type Error = IndexerEventParseError;
+impl TryFrom<u64> for IndexerEventKind {
+ type Error = IndexerEventKindParseError;
fn try_from(val: u64) -> Result<Self, Self::Error> {
match val {
- 0 => Ok(IndexerEvent::Metadata),
- other => Err(IndexerEventParseError(other)),
+ 0 => Ok(IndexerEventKind::Metadata),
+ other => Err(IndexerEventKindParseError(other)),
}
}
}
-impl Serialize for IndexerEvent {
+impl Serialize for IndexerEventKind {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
diff --git a/crates/indexer/src/domain/event/mod.rs b/crates/indexer/src/domain/event/mod.rs
@@ -2,4 +2,4 @@ mod key;
mod kind;
pub use key::{IndexerKey, METADATA_INDEX_DIRECTORY};
-pub use kind::{IndexerEvent, IndexerEventParseError};
+pub use kind::{IndexerEventKind, IndexerEventKindParseError};
diff --git a/crates/indexer/src/domain/indexer.rs b/crates/indexer/src/domain/indexer.rs
@@ -1,10 +1,10 @@
use anyhow::{Context, Result};
use indexer_utils::file::fs_mkdir;
-use crate::{config::Settings, IndexerEvent};
+use crate::{config::Settings, IndexerEventKind};
pub fn create_index_dirs(settings: &Settings) -> Result<()> {
- for kind in IndexerEvent::ALL {
+ for kind in IndexerEventKind::ALL {
let kind_str = kind.as_u64().to_string();
for subdir in kind.paths() {
diff --git a/crates/indexer/src/lib.rs b/crates/indexer/src/lib.rs
@@ -1,6 +1,9 @@
use anyhow::{Context, Result};
use indexer_utils::sqlite::{sqlite_conn, sqlite_stmt};
-use std::time::{Duration, Instant};
+use std::{
+ collections::HashMap,
+ time::{Duration, Instant},
+};
use tracing::info;
pub mod cli;
@@ -13,15 +16,18 @@ pub mod domain {
}
pub mod relay {
- pub mod model;
+ pub mod event;
+ pub mod record;
}
pub use config::Settings;
-pub use domain::event::{IndexerEvent, IndexerKey};
-pub use relay::model::RelayEventRecord;
+pub use domain::event::{IndexerEventKind, IndexerKey};
+pub use relay::record::RelayEventRecord;
+
+use crate::relay::event::RelayIndexerEvent;
pub async fn run(settings: Settings) -> Result<()> {
- let select_event_kinds = IndexerEvent::ALL
+ let select_event_kinds = IndexerEventKind::ALL
.iter()
.map(|k| k.as_u64().to_string())
.collect::<Vec<_>>()
@@ -52,6 +58,24 @@ pub async fn run(settings: Settings) -> Result<()> {
info!(record_count = records.len(), "Loaded RelayEventRecords");
+ let records_by_kind: HashMap<IndexerEventKind, Vec<RelayIndexerEvent>> = records
+ .into_iter()
+ .map(RelayIndexerEvent::try_from)
+ .collect::<Result<Vec<_>>>()?
+ .into_iter()
+ .fold(
+ HashMap::<IndexerEventKind, Vec<RelayIndexerEvent>>::new(),
+ |mut acc, ev| {
+ acc.entry(ev.kind).or_default().push(ev);
+ acc
+ },
+ );
+
+ info!(
+ records_count_by_kind = records_by_kind.len(),
+ "Loaded RelayIndexerEvents"
+ );
+
// sleep
let elapsed = iteration_start.elapsed();
let interval = Duration::from_secs(settings.service.flush_interval);
diff --git a/crates/indexer/src/relay/event.rs b/crates/indexer/src/relay/event.rs
@@ -0,0 +1,52 @@
+use anyhow::{Context, Result};
+use serde::{Deserialize, Serialize};
+
+use crate::{domain::event::IndexerEventKindParseError, IndexerEventKind, RelayEventRecord};
+
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct RelayRawEvent {
+ pub id: String,
+ pub pubkey: String,
+ pub created_at: u32,
+ pub kind: u32,
+ pub tags: Vec<Vec<String>>,
+ pub content: String,
+ pub sig: String,
+}
+
+#[derive(Debug, Clone, Serialize)]
+pub struct RelayIndexerEvent {
+ pub id: String,
+ pub author: String,
+ pub created_at: u32,
+ pub pubkey: String,
+ pub kind: IndexerEventKind,
+ pub tags: Vec<Vec<String>>,
+ pub content: String,
+ pub hash: String,
+ pub sig: String,
+}
+
+impl TryFrom<RelayEventRecord> for RelayIndexerEvent {
+ type Error = anyhow::Error;
+
+ fn try_from(rec: RelayEventRecord) -> Result<Self> {
+ let raw: RelayRawEvent = serde_json::from_str(&rec.content)
+ .with_context(|| format!("Failed to parse relay JSON for event {}", rec.event_hash))?;
+
+ let kind = IndexerEventKind::try_from(raw.kind as u64)
+ .map_err(|e: IndexerEventKindParseError| anyhow::anyhow!(e))?;
+
+ Ok(RelayIndexerEvent {
+ id: raw.id.to_lowercase(),
+ author: rec.author.to_lowercase(),
+ created_at: raw.created_at,
+ pubkey: raw.pubkey.to_lowercase(),
+ kind,
+ tags: raw.tags,
+ content: raw.content,
+ hash: rec.event_hash,
+ sig: raw.sig.to_lowercase(),
+ })
+ }
+}
diff --git a/crates/indexer/src/relay/model.rs b/crates/indexer/src/relay/model.rs
@@ -1,36 +0,0 @@
-use indexer_utils::sqlite::{RustqliteError, SqliteResult, SqliteRow, SqliteType};
-use serde::Serialize;
-
-use crate::domain::event::{IndexerEvent, IndexerEventParseError};
-
-#[derive(Clone, Debug, Serialize)]
-pub struct RelayEventRecord {
- pub event_hash: String,
- pub author: String,
- pub created_at: u32,
- pub kind: IndexerEvent,
- pub content: String,
-}
-
-impl RelayEventRecord {
- pub fn from_row(row: &SqliteRow) -> SqliteResult<Self> {
- let event_hash: String = row.get(0)?;
- let author: String = row.get(1)?;
- let created_at: u32 = row.get(2)?;
- let kind_num: u32 = row.get(3)?;
-
- let kind =
- IndexerEvent::try_from(kind_num as u64).map_err(|e: IndexerEventParseError| {
- RustqliteError::FromSqlConversionFailure(3, SqliteType::Integer, Box::new(e))
- })?;
-
- let content: String = row.get(4)?;
- Ok(RelayEventRecord {
- event_hash,
- author,
- created_at,
- kind,
- content,
- })
- }
-}
diff --git a/crates/indexer/src/relay/record.rs b/crates/indexer/src/relay/record.rs
@@ -0,0 +1,37 @@
+use indexer_utils::sqlite::{RustqliteError, SqliteResult, SqliteRow, SqliteType};
+use serde::Serialize;
+
+use crate::domain::event::{IndexerEventKind, IndexerEventKindParseError};
+
+#[derive(Clone, Debug, Serialize)]
+pub struct RelayEventRecord {
+ pub event_hash: String,
+ pub author: String,
+ pub created_at: u32,
+ pub kind: IndexerEventKind,
+ pub content: String,
+}
+
+impl RelayEventRecord {
+ pub fn from_row(row: &SqliteRow) -> SqliteResult<Self> {
+ let event_hash: String = row.get(0)?;
+ let author: String = row.get(1)?;
+ let created_at: u32 = row.get(2)?;
+ let kind_num: u32 = row.get(3)?;
+
+ let kind = IndexerEventKind::try_from(kind_num as u64).map_err(
+ |e: IndexerEventKindParseError| {
+ RustqliteError::FromSqlConversionFailure(3, SqliteType::Integer, Box::new(e))
+ },
+ )?;
+
+ let content: String = row.get(4)?;
+ Ok(RelayEventRecord {
+ event_hash,
+ author,
+ created_at,
+ kind,
+ content,
+ })
+ }
+}