Skip to content

Commit

Permalink
Add nostr-sdk-db crate
Browse files Browse the repository at this point in the history
  • Loading branch information
yukibtc committed Oct 12, 2023
1 parent 36e39f4 commit aaa8941
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 3 deletions.
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ members = [
"bindings/nostr-sdk-ffi",
"bindings/nostr-sdk-js",
"bindings/uniffi-bindgen",
"crates/nostr",
"crates/nostr-sdk",
"crates/nostr-sdk-net",
"crates/*",
]
default-members = ["crates/*"]
resolver = "2"
Expand Down
18 changes: 18 additions & 0 deletions crates/nostr-sdk-db/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "nostr-sdk-db"
version = "0.1.0"
edition = "2021"
description = "Nostr SDK Database"
authors = ["Yuki Kishimoto <[email protected]>"]
homepage.workspace = true
repository.workspace = true
license.workspace = true
readme = "README.md"
rust-version.workspace = true
keywords = ["nostr", "sdk", "db"]

[dependencies]
async-trait = "0.1"
nostr = { version = "0.24", path = "../nostr", default-features = false, features = ["std"] }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["sync"] }
27 changes: 27 additions & 0 deletions crates/nostr-sdk-db/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2022-2023 Yuki Kishimoto
// Distributed under the MIT software license

//! Database Error
use thiserror::Error;

/// Database Error
#[derive(Debug, Error)]
pub enum DatabaseError {
/// An error happened in the underlying database backend.
#[error(transparent)]
Backend(Box<dyn std::error::Error + Send + Sync>),
}

impl DatabaseError {
/// Create a new [`Backend`][Self::Backend] error.
///
/// Shorthand for `Error::Backend(Box::new(error))`.
#[inline]
pub fn backend<E>(error: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self::Backend(Box::new(error))
}
}
102 changes: 102 additions & 0 deletions crates/nostr-sdk-db/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright (c) 2022-2023 Yuki Kishimoto
// Distributed under the MIT software license

//! Nostr SDK Database
#![warn(missing_docs)]
#![warn(rustdoc::bare_urls)]

use async_trait::async_trait;
use nostr::{Event, EventId, Filter, Url};

mod error;
pub mod memory;

pub use self::error::DatabaseError;

/// Backend
pub enum Backend {
/// Memory
Memory,
/// RocksDB
RocksDB,
/// Lightning Memory-Mapped Database
LMDB,
/// SQLite
SQLite,
/// IndexedDB
IndexedDB,
/// Custom
Custom(String),
}

/// A type-erased [`StateStore`].
pub type DynNostrDatabase = dyn NostrDatabase<Err = DatabaseError>;

/// Nostr SDK Database
#[async_trait]
pub trait NostrDatabase: AsyncTraitDeps {
/// Error
type Err;

/// Name of the backend database used (ex. rocksdb, lmdb, sqlite, indexeddb, ...)
fn backend(&self) -> Backend;

/// Save [`Event`] into store
async fn save_event(&self, event: &Event) -> Result<(), Self::Err>;

/// Save [`EventId`] seen by relay
///
/// Useful for NIP65 (gossip)
async fn save_event_id_seen_by_relay(
&self,
event_id: EventId,
relay_url: Url,
) -> Result<(), Self::Err>;

/// Get list of relays that have seen the [`EventId`]
async fn event_recently_seen_on_relays(&self, event_id: EventId)
-> Result<Vec<Url>, Self::Err>;

/// Query store with filters
async fn query(&self, filters: Vec<Filter>) -> Result<Vec<Event>, Self::Err>;

/// Get event IDs by filters
///
/// Uuseful for negentropy reconciliation
async fn event_ids_by_filters(&self, filters: Vec<Filter>) -> Result<Vec<EventId>, Self::Err>;
}

/// Alias for `Send` on non-wasm, empty trait (implemented by everything) on
/// wasm.
#[cfg(not(target_arch = "wasm32"))]
pub trait SendOutsideWasm: Send {}
#[cfg(not(target_arch = "wasm32"))]
impl<T: Send> SendOutsideWasm for T {}

/// Alias for `Send` on non-wasm, empty trait (implemented by everything) on
/// wasm.
#[cfg(target_arch = "wasm32")]
pub trait SendOutsideWasm {}
#[cfg(target_arch = "wasm32")]
impl<T> SendOutsideWasm for T {}

/// Alias for `Sync` on non-wasm, empty trait (implemented by everything) on
/// wasm.
#[cfg(not(target_arch = "wasm32"))]
pub trait SyncOutsideWasm: Sync {}
#[cfg(not(target_arch = "wasm32"))]
impl<T: Sync> SyncOutsideWasm for T {}

/// Alias for `Sync` on non-wasm, empty trait (implemented by everything) on
/// wasm.
#[cfg(target_arch = "wasm32")]
pub trait SyncOutsideWasm {}
#[cfg(target_arch = "wasm32")]
impl<T> SyncOutsideWasm for T {}

/// Super trait that is used for our store traits, this trait will differ if
/// it's used on WASM. WASM targets will not require `Send` and `Sync` to have
/// implemented, while other targets will.
pub trait AsyncTraitDeps: std::fmt::Debug + SendOutsideWasm + SyncOutsideWasm {}
impl<T: std::fmt::Debug + SendOutsideWasm + SyncOutsideWasm> AsyncTraitDeps for T {}
84 changes: 84 additions & 0 deletions crates/nostr-sdk-db/src/memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) 2022-2023 Yuki Kishimoto
// Distributed under the MIT software license

//! Nostr SDK Database
use std::collections::{HashMap, HashSet};
use std::sync::Arc;

use async_trait::async_trait;
use nostr::{Event, EventId, Filter, Url};
use thiserror::Error;
use tokio::sync::RwLock;

use crate::{Backend, DatabaseError, NostrDatabase};

/// Memory Database Error
#[derive(Debug, Error)]
pub enum Error {}

impl From<Error> for DatabaseError {
fn from(e: Error) -> Self {
DatabaseError::backend(e)
}
}

/// Memory Database (RAM)
#[derive(Debug, Default)]
pub struct MemoryDatabase {
seen_event_ids: Arc<RwLock<HashMap<EventId, HashSet<Url>>>>,
}

impl MemoryDatabase {
/// New Memory database
pub fn new() -> Self {
Self::default()
}
}

#[async_trait]
impl NostrDatabase for MemoryDatabase {
type Err = DatabaseError;

fn backend(&self) -> Backend {
Backend::Memory
}

async fn save_event(&self, _event: &Event) -> Result<(), Self::Err> {
Ok(())
}

async fn save_event_id_seen_by_relay(
&self,
event_id: EventId,
relay_url: Url,
) -> Result<(), Self::Err> {
let mut seen_event_ids = self.seen_event_ids.write().await;
seen_event_ids
.entry(event_id)
.and_modify(|set| {
set.insert(relay_url.clone());
})
.or_insert_with(|| {
let mut set = HashSet::with_capacity(1);
set.insert(relay_url);
set
});
Ok(())
}

async fn event_recently_seen_on_relays(
&self,
_event_id: EventId,
) -> Result<Vec<Url>, Self::Err> {
todo!()
}

async fn query(&self, _filters: Vec<Filter>) -> Result<Vec<Event>, Self::Err> {
Ok(Vec::new())
}

async fn event_ids_by_filters(&self, _filters: Vec<Filter>) -> Result<Vec<EventId>, Self::Err> {
Ok(Vec::new())
}
}

0 comments on commit aaa8941

Please sign in to comment.