Skip to content

Commit 06dd1b6

Browse files
committed
database: merge traits into NostrDatabase
Merge `NostrEventsDatabase` and `NostrDatabaseWipe` into `NostrDatabase` Pull-Request: #916 Signed-off-by: Yuki Kishimoto <[email protected]>
1 parent 4096b9d commit 06dd1b6

File tree

15 files changed

+129
-195
lines changed

15 files changed

+129
-195
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
- nostr: update `Nip19Profile::new` and `Nip19Coordinate::new` signature ([Yuki Kishimoto] at https://github.com/rust-nostr/nostr/pull/910)
3434
- nostr: update `RelayInformationDocument::get` signature ([Yuki Kishimoto] at https://github.com/rust-nostr/nostr/pull/913)
3535
- connect: remove `NostrConnect::get_relays` ([Yuki Kishimoto] at https://github.com/rust-nostr/nostr/pull/894)
36+
- database: merge traits into `NostrDatabase` ([Yuki Kishimoto] at https://github.com/rust-nostr/nostr/pull/916)
3637

3738
### Changed
3839

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The project is split up into several crates in the `crates/` directory:
88
* [**nostr**](./crates/nostr): Rust implementation of Nostr protocol
99
* [**nostr-blossom**](./crates/nostr-blossom): A library for interacting with the Blossom protocol
1010
* [**nostr-connect**](./crates/nostr-connect): Nostr Connect (NIP46)
11-
* [**nostr-database**](./crates/nostr-database): Database for Nostr apps
11+
* [**nostr-database**](./crates/nostr-database): Events database abstraction and in-memory implementation
1212
* [**nostr-lmdb**](./crates/nostr-lmdb): LMDB storage backend
1313
* [**nostr-ndb**](./crates/nostr-ndb): [nostrdb](https://github.com/damus-io/nostrdb) storage backend
1414
* [**nostr-indexeddb**](./crates/nostr-indexeddb): IndexedDB storage backend

crates/nostr-database/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "nostr-database"
33
version = "0.42.0"
44
edition = "2021"
5-
description = "Database for Nostr apps"
5+
description = "Events database abstraction and in-memory implementation"
66
authors.workspace = true
77
homepage.workspace = true
88
repository.workspace = true

crates/nostr-database/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Nostr Database
1+
# Nostr (Events) Database
22

3-
Database abstraction and in-memory implementation for nostr apps
3+
Events database abstraction and in-memory implementation for nostr apps.
44

55
## Nostr Database Trait
66

crates/nostr-database/src/events/mod.rs renamed to crates/nostr-database/src/ext.rs

Lines changed: 5 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -2,154 +2,16 @@
22
// Copyright (c) 2023-2025 Rust Nostr Developers
33
// Distributed under the MIT software license
44

5+
//! Nostr database extension
6+
57
use std::collections::{BTreeSet, HashMap, HashSet};
6-
use std::fmt;
7-
use std::sync::Arc;
88

99
use nostr::prelude::*;
1010

11-
pub mod helper;
12-
13-
use crate::{DatabaseError, Events, Profile};
14-
15-
/// NIP65 relays map
16-
pub type RelaysMap = HashMap<RelayUrl, Option<RelayMetadata>>;
17-
18-
/// Database event status
19-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
20-
pub enum DatabaseEventStatus {
21-
/// The event is saved into the database
22-
Saved,
23-
/// The event is marked as deleted
24-
Deleted,
25-
/// The event doesn't exist
26-
NotExistent,
27-
}
28-
29-
/// Reason why event wasn't stored into the database
30-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
31-
pub enum RejectedReason {
32-
/// Ephemeral events aren't expected to be stored
33-
Ephemeral,
34-
/// The event already exists
35-
Duplicate,
36-
/// The event was deleted
37-
Deleted,
38-
/// The event is expired
39-
Expired,
40-
/// The event was replaced
41-
Replaced,
42-
/// Attempt to delete a non-owned event
43-
InvalidDelete,
44-
/// Other reason
45-
Other,
46-
}
47-
48-
/// Save event status
49-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
50-
pub enum SaveEventStatus {
51-
/// The event has been successfully saved
52-
Success,
53-
/// The event has been rejected
54-
Rejected(RejectedReason),
55-
}
56-
57-
impl SaveEventStatus {
58-
/// Check if event is successfully saved
59-
#[inline]
60-
pub fn is_success(&self) -> bool {
61-
matches!(self, Self::Success)
62-
}
63-
}
64-
65-
#[doc(hidden)]
66-
pub trait IntoNostrEventsDatabase {
67-
fn into_database(self) -> Arc<dyn NostrEventsDatabase>;
68-
}
69-
70-
impl IntoNostrEventsDatabase for Arc<dyn NostrEventsDatabase> {
71-
fn into_database(self) -> Arc<dyn NostrEventsDatabase> {
72-
self
73-
}
74-
}
75-
76-
impl<T> IntoNostrEventsDatabase for T
77-
where
78-
T: NostrEventsDatabase + Sized + 'static,
79-
{
80-
fn into_database(self) -> Arc<dyn NostrEventsDatabase> {
81-
Arc::new(self)
82-
}
83-
}
84-
85-
impl<T> IntoNostrEventsDatabase for Arc<T>
86-
where
87-
T: NostrEventsDatabase + 'static,
88-
{
89-
fn into_database(self) -> Arc<dyn NostrEventsDatabase> {
90-
self
91-
}
92-
}
93-
94-
/// Nostr Events Database
95-
///
96-
/// Store for the nostr events.
97-
pub trait NostrEventsDatabase: fmt::Debug + Send + Sync {
98-
/// Save [`Event`] into store
99-
///
100-
/// **This method assumes that [`Event`] was already verified**
101-
fn save_event<'a>(
102-
&'a self,
103-
event: &'a Event,
104-
) -> BoxedFuture<'a, Result<SaveEventStatus, DatabaseError>>;
105-
106-
/// Check event status by ID
107-
///
108-
/// Check if the event is saved, deleted or not existent.
109-
fn check_id<'a>(
110-
&'a self,
111-
event_id: &'a EventId,
112-
) -> BoxedFuture<'a, Result<DatabaseEventStatus, DatabaseError>>;
113-
114-
// TODO: rename to `check_coordinate`?
115-
/// Check if [`Coordinate`] has been deleted before a certain [`Timestamp`]
116-
fn has_coordinate_been_deleted<'a>(
117-
&'a self,
118-
coordinate: &'a CoordinateBorrow<'a>,
119-
timestamp: &'a Timestamp,
120-
) -> BoxedFuture<'a, Result<bool, DatabaseError>>;
121-
122-
/// Get [`Event`] by [`EventId`]
123-
fn event_by_id<'a>(
124-
&'a self,
125-
event_id: &'a EventId,
126-
) -> BoxedFuture<'a, Result<Option<Event>, DatabaseError>>;
127-
128-
/// Count the number of events found with [`Filter`].
129-
///
130-
/// Use `Filter::new()` or `Filter::default()` to count all events.
131-
fn count(&self, filter: Filter) -> BoxedFuture<Result<usize, DatabaseError>>;
132-
133-
/// Query stored events.
134-
fn query(&self, filter: Filter) -> BoxedFuture<Result<Events, DatabaseError>>;
135-
136-
/// Get `negentropy` items
137-
fn negentropy_items(
138-
&self,
139-
filter: Filter,
140-
) -> BoxedFuture<Result<Vec<(EventId, Timestamp)>, DatabaseError>> {
141-
Box::pin(async move {
142-
let events: Events = self.query(filter).await?;
143-
Ok(events.into_iter().map(|e| (e.id, e.created_at)).collect())
144-
})
145-
}
146-
147-
/// Delete all events that match the [Filter]
148-
fn delete(&self, filter: Filter) -> BoxedFuture<Result<(), DatabaseError>>;
149-
}
11+
use crate::{DatabaseError, Events, NostrDatabase, Profile, RelaysMap};
15012

15113
/// Nostr Event Store Extension
152-
pub trait NostrEventsDatabaseExt: NostrEventsDatabase {
14+
pub trait NostrDatabaseExt: NostrDatabase {
15315
/// Get public key metadata
15416
fn metadata(
15517
&self,
@@ -275,4 +137,4 @@ pub trait NostrEventsDatabaseExt: NostrEventsDatabase {
275137
}
276138
}
277139

278-
impl<T: NostrEventsDatabase + ?Sized> NostrEventsDatabaseExt for T {}
140+
impl<T: NostrDatabase + ?Sized> NostrDatabaseExt for T {}
File renamed without changes.

crates/nostr-database/src/lib.rs

Lines changed: 113 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,33 @@
99
#![warn(clippy::large_futures)]
1010
#![allow(clippy::mutable_key_type)] // TODO: remove when possible. Needed to suppress false positive for `BTreeSet<Event>`
1111

12+
use std::collections::HashMap;
13+
use std::fmt::Debug;
1214
use std::sync::Arc;
1315

1416
pub use nostr;
17+
use nostr::prelude::*;
1518

1619
mod collections;
1720
mod error;
18-
mod events;
21+
pub mod ext;
1922
#[cfg(feature = "flatbuf")]
2023
pub mod flatbuffers;
24+
mod helper;
2125
pub mod memory;
2226
pub mod prelude;
2327
pub mod profile;
24-
mod wipe;
2528

2629
pub use self::collections::events::Events;
2730
pub use self::error::DatabaseError;
28-
pub use self::events::helper::{DatabaseEventResult, DatabaseHelper};
29-
pub use self::events::{
30-
DatabaseEventStatus, IntoNostrEventsDatabase, NostrEventsDatabase, NostrEventsDatabaseExt,
31-
RejectedReason, SaveEventStatus,
32-
};
3331
#[cfg(feature = "flatbuf")]
3432
pub use self::flatbuffers::{FlatBufferBuilder, FlatBufferDecode, FlatBufferEncode};
33+
pub use self::helper::{DatabaseEventResult, DatabaseHelper};
3534
pub use self::memory::{MemoryDatabase, MemoryDatabaseOptions};
3635
pub use self::profile::Profile;
37-
pub use self::wipe::NostrDatabaseWipe;
36+
37+
/// NIP65 relays map
38+
pub type RelaysMap = HashMap<RelayUrl, Option<RelayMetadata>>;
3839

3940
/// Backend
4041
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -62,6 +63,53 @@ impl Backend {
6263
}
6364
}
6465

66+
/// Database event status
67+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
68+
pub enum DatabaseEventStatus {
69+
/// The event is saved into the database
70+
Saved,
71+
/// The event is marked as deleted
72+
Deleted,
73+
/// The event doesn't exist
74+
NotExistent,
75+
}
76+
77+
/// Reason why event wasn't stored into the database
78+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
79+
pub enum RejectedReason {
80+
/// Ephemeral events aren't expected to be stored
81+
Ephemeral,
82+
/// The event already exists
83+
Duplicate,
84+
/// The event was deleted
85+
Deleted,
86+
/// The event is expired
87+
Expired,
88+
/// The event was replaced
89+
Replaced,
90+
/// Attempt to delete a non-owned event
91+
InvalidDelete,
92+
/// Other reason
93+
Other,
94+
}
95+
96+
/// Save event status
97+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
98+
pub enum SaveEventStatus {
99+
/// The event has been successfully saved
100+
Success,
101+
/// The event has been rejected
102+
Rejected(RejectedReason),
103+
}
104+
105+
impl SaveEventStatus {
106+
/// Check if event is successfully saved
107+
#[inline]
108+
pub fn is_success(&self) -> bool {
109+
matches!(self, Self::Success)
110+
}
111+
}
112+
65113
#[doc(hidden)]
66114
pub trait IntoNostrDatabase {
67115
fn into_nostr_database(self) -> Arc<dyn NostrDatabase>;
@@ -91,10 +139,65 @@ where
91139
}
92140
}
93141

94-
/// Nostr Database
95-
pub trait NostrDatabase: NostrEventsDatabase + NostrDatabaseWipe {
142+
/// Nostr (Events) Database
143+
pub trait NostrDatabase: Debug + Send + Sync {
96144
/// Name of the backend database used
97145
fn backend(&self) -> Backend;
146+
147+
/// Save [`Event`] into store
148+
///
149+
/// **This method assumes that [`Event`] was already verified**
150+
fn save_event<'a>(
151+
&'a self,
152+
event: &'a Event,
153+
) -> BoxedFuture<'a, Result<SaveEventStatus, DatabaseError>>;
154+
155+
/// Check event status by ID
156+
///
157+
/// Check if the event is saved, deleted or not existent.
158+
fn check_id<'a>(
159+
&'a self,
160+
event_id: &'a EventId,
161+
) -> BoxedFuture<'a, Result<DatabaseEventStatus, DatabaseError>>;
162+
163+
// TODO: rename to `check_coordinate`?
164+
/// Check if [`Coordinate`] has been deleted before a certain [`Timestamp`]
165+
fn has_coordinate_been_deleted<'a>(
166+
&'a self,
167+
coordinate: &'a CoordinateBorrow<'a>,
168+
timestamp: &'a Timestamp,
169+
) -> BoxedFuture<'a, Result<bool, DatabaseError>>;
170+
171+
/// Get [`Event`] by [`EventId`]
172+
fn event_by_id<'a>(
173+
&'a self,
174+
event_id: &'a EventId,
175+
) -> BoxedFuture<'a, Result<Option<Event>, DatabaseError>>;
176+
177+
/// Count the number of events found with [`Filter`].
178+
///
179+
/// Use `Filter::new()` or `Filter::default()` to count all events.
180+
fn count(&self, filter: Filter) -> BoxedFuture<Result<usize, DatabaseError>>;
181+
182+
/// Query stored events.
183+
fn query(&self, filter: Filter) -> BoxedFuture<Result<Events, DatabaseError>>;
184+
185+
/// Get `negentropy` items
186+
fn negentropy_items(
187+
&self,
188+
filter: Filter,
189+
) -> BoxedFuture<Result<Vec<(EventId, Timestamp)>, DatabaseError>> {
190+
Box::pin(async move {
191+
let events: Events = self.query(filter).await?;
192+
Ok(events.into_iter().map(|e| (e.id, e.created_at)).collect())
193+
})
194+
}
195+
196+
/// Delete all events that match the [Filter]
197+
fn delete(&self, filter: Filter) -> BoxedFuture<Result<(), DatabaseError>>;
198+
199+
/// Wipe all data
200+
fn wipe(&self) -> BoxedFuture<Result<(), DatabaseError>>;
98201
}
99202

100203
#[cfg(test)]

crates/nostr-database/src/memory.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use tokio::sync::RwLock;
1313

1414
use crate::{
1515
Backend, DatabaseError, DatabaseEventResult, DatabaseEventStatus, DatabaseHelper, Events,
16-
NostrDatabase, NostrDatabaseWipe, NostrEventsDatabase, SaveEventStatus,
16+
NostrDatabase, SaveEventStatus,
1717
};
1818

1919
const MAX_EVENTS: usize = 35_000;
@@ -107,9 +107,7 @@ impl NostrDatabase for MemoryDatabase {
107107
fn backend(&self) -> Backend {
108108
Backend::Memory
109109
}
110-
}
111110

112-
impl NostrEventsDatabase for MemoryDatabase {
113111
fn save_event<'a>(
114112
&'a self,
115113
event: &'a Event,
@@ -227,9 +225,7 @@ impl NostrEventsDatabase for MemoryDatabase {
227225
}
228226
})
229227
}
230-
}
231228

232-
impl NostrDatabaseWipe for MemoryDatabase {
233229
fn wipe(&self) -> BoxedFuture<Result<(), DatabaseError>> {
234230
Box::pin(async move {
235231
match &self.inner {

0 commit comments

Comments
 (0)