1#![warn(missing_docs)]
8#![warn(rustdoc::bare_urls)]
9#![warn(clippy::large_futures)]
10#![allow(clippy::mutable_key_type)] use std::any::Any;
13use std::collections::HashMap;
14use std::fmt::Debug;
15use std::sync::Arc;
16
17pub use nostr;
18use nostr::prelude::*;
19
20mod collections;
21mod error;
22pub mod ext;
23#[cfg(feature = "flatbuf")]
24pub mod flatbuffers;
25mod helper;
26pub mod memory;
27pub mod prelude;
28pub mod profile;
29
30pub use self::collections::events::Events;
31pub use self::error::DatabaseError;
32#[cfg(feature = "flatbuf")]
33pub use self::flatbuffers::{FlatBufferBuilder, FlatBufferDecode, FlatBufferEncode};
34pub use self::helper::{DatabaseEventResult, DatabaseHelper};
35pub use self::memory::{MemoryDatabase, MemoryDatabaseOptions};
36pub use self::profile::Profile;
37
38pub type RelaysMap = HashMap<RelayUrl, Option<RelayMetadata>>;
40
41#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum Backend {
44 Memory,
46 RocksDB,
48 LMDB,
50 SQLite,
52 IndexedDB,
54 Custom(String),
56}
57
58impl Backend {
59 pub fn is_persistent(&self) -> bool {
63 !matches!(self, Self::Memory)
64 }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
69pub enum DatabaseEventStatus {
70 Saved,
72 Deleted,
74 NotExistent,
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
80pub enum RejectedReason {
81 Ephemeral,
83 Duplicate,
85 Deleted,
87 Expired,
89 Replaced,
91 InvalidDelete,
93 Other,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
99pub enum SaveEventStatus {
100 Success,
102 Rejected(RejectedReason),
104}
105
106impl SaveEventStatus {
107 #[inline]
109 pub fn is_success(&self) -> bool {
110 matches!(self, Self::Success)
111 }
112}
113
114#[doc(hidden)]
115pub trait IntoNostrDatabase {
116 fn into_nostr_database(self) -> Arc<dyn NostrDatabase>;
117}
118
119impl IntoNostrDatabase for Arc<dyn NostrDatabase> {
120 fn into_nostr_database(self) -> Arc<dyn NostrDatabase> {
121 self
122 }
123}
124
125impl<T> IntoNostrDatabase for T
126where
127 T: NostrDatabase + Sized + 'static,
128{
129 fn into_nostr_database(self) -> Arc<dyn NostrDatabase> {
130 Arc::new(self)
131 }
132}
133
134impl<T> IntoNostrDatabase for Arc<T>
135where
136 T: NostrDatabase + 'static,
137{
138 fn into_nostr_database(self) -> Arc<dyn NostrDatabase> {
139 self
140 }
141}
142
143pub trait NostrDatabase: Any + Debug + Send + Sync {
145 fn backend(&self) -> Backend;
147
148 fn save_event<'a>(
152 &'a self,
153 event: &'a Event,
154 ) -> BoxedFuture<'a, Result<SaveEventStatus, DatabaseError>>;
155
156 fn check_id<'a>(
160 &'a self,
161 event_id: &'a EventId,
162 ) -> BoxedFuture<'a, Result<DatabaseEventStatus, DatabaseError>>;
163
164 fn event_by_id<'a>(
166 &'a self,
167 event_id: &'a EventId,
168 ) -> BoxedFuture<'a, Result<Option<Event>, DatabaseError>>;
169
170 fn count(&self, filter: Filter) -> BoxedFuture<Result<usize, DatabaseError>>;
174
175 fn query(&self, filter: Filter) -> BoxedFuture<Result<Events, DatabaseError>>;
177
178 fn negentropy_items(
180 &self,
181 filter: Filter,
182 ) -> BoxedFuture<Result<Vec<(EventId, Timestamp)>, DatabaseError>> {
183 Box::pin(async move {
184 let events: Events = self.query(filter).await?;
185 Ok(events.into_iter().map(|e| (e.id, e.created_at)).collect())
186 })
187 }
188
189 fn delete(&self, filter: Filter) -> BoxedFuture<Result<(), DatabaseError>>;
191
192 fn wipe(&self) -> BoxedFuture<Result<(), DatabaseError>>;
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_backend_is_persistent() {
202 assert!(!Backend::Memory.is_persistent());
203 assert!(Backend::RocksDB.is_persistent());
204 assert!(Backend::LMDB.is_persistent());
205 assert!(Backend::SQLite.is_persistent());
206 assert!(Backend::IndexedDB.is_persistent());
207 assert!(Backend::Custom("custom".to_string()).is_persistent());
208 }
209}