1#![forbid(unsafe_code)]
8#![warn(missing_docs)]
9#![warn(rustdoc::bare_urls)]
10#![warn(clippy::large_futures)]
11#![cfg_attr(bench, feature(test))]
12
13#[cfg(bench)]
14extern crate test;
15
16use std::any::Any;
17use std::collections::BTreeSet;
18use std::fmt::Debug;
19use std::future::Future;
20use std::pin::Pin;
21use std::sync::Arc;
22
23use nostr::event::{Event, EventId};
24use nostr::filter::Filter;
25use nostr::types::Timestamp;
26
27pub mod error;
28pub mod prelude;
29
30use self::error::Error;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub struct Features {
35 pub persistent: bool,
37 pub event_expiration: bool,
44 pub full_text_search: bool,
48 pub request_to_vanish: bool,
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
56pub enum DatabaseEventStatus {
57 Saved,
59 Deleted,
61 NotExistent,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
67pub enum RejectedReason {
68 Ephemeral,
70 Duplicate,
72 Deleted,
74 Expired,
76 Replaced,
78 InvalidDelete,
80 Vanished,
82 Other,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
88pub enum SaveEventStatus {
89 Success,
91 Rejected(RejectedReason),
93}
94
95impl SaveEventStatus {
96 #[inline]
98 pub fn is_success(&self) -> bool {
99 matches!(self, Self::Success)
100 }
101}
102
103#[doc(hidden)]
104pub trait IntoNostrDatabase {
105 fn into_nostr_database(self) -> Arc<dyn NostrDatabase>;
106}
107
108impl IntoNostrDatabase for Arc<dyn NostrDatabase> {
109 fn into_nostr_database(self) -> Arc<dyn NostrDatabase> {
110 self
111 }
112}
113
114impl<T> IntoNostrDatabase for T
115where
116 T: NostrDatabase + Sized + 'static,
117{
118 fn into_nostr_database(self) -> Arc<dyn NostrDatabase> {
119 Arc::new(self)
120 }
121}
122
123impl<T> IntoNostrDatabase for Arc<T>
124where
125 T: NostrDatabase + 'static,
126{
127 fn into_nostr_database(self) -> Arc<dyn NostrDatabase> {
128 self
129 }
130}
131
132pub trait NostrDatabase: Any + Debug + Send + Sync {
134 fn backend(&self) -> &'static str;
136
137 fn features(&self) -> Features;
139
140 fn save_event<'a>(
144 &'a self,
145 event: &'a Event,
146 ) -> Pin<Box<dyn Future<Output = Result<SaveEventStatus, Error>> + Send + 'a>>;
147
148 fn check_id<'a>(
152 &'a self,
153 event_id: &'a EventId,
154 ) -> Pin<Box<dyn Future<Output = Result<DatabaseEventStatus, Error>> + Send + 'a>>;
155
156 fn event_by_id<'a>(
158 &'a self,
159 event_id: &'a EventId,
160 ) -> Pin<Box<dyn Future<Output = Result<Option<Event>, Error>> + Send + 'a>>;
161
162 fn count(
166 &self,
167 filter: Filter,
168 ) -> Pin<Box<dyn Future<Output = Result<usize, Error>> + Send + '_>>;
169
170 fn query(
172 &self,
173 filter: Filter,
174 ) -> Pin<Box<dyn Future<Output = Result<BTreeSet<Event>, Error>> + Send + '_>>;
175
176 #[allow(clippy::type_complexity)]
178 fn negentropy_items(
179 &self,
180 filter: Filter,
181 ) -> Pin<Box<dyn Future<Output = Result<Vec<(EventId, Timestamp)>, Error>> + Send + '_>> {
182 Box::pin(async move {
183 let events: BTreeSet<Event> = self.query(filter).await?;
184 let now = Timestamp::now();
185 Ok(events
186 .into_iter()
187 .filter(|event| !event.is_expired_at(now))
188 .map(|event| (event.id, event.created_at))
189 .collect())
190 })
191 }
192
193 fn delete(
195 &self,
196 filter: Filter,
197 ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + '_>>;
198
199 fn wipe(&self) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + '_>>;
201}