1use std::num::NonZeroUsize;
8use std::sync::Arc;
9
10use lru::LruCache;
11use nostr::prelude::*;
12use tokio::sync::RwLock;
13
14use crate::{
15 Backend, DatabaseError, DatabaseEventResult, DatabaseEventStatus, DatabaseHelper, Events,
16 NostrDatabase, SaveEventStatus,
17};
18
19const MAX_EVENTS: usize = 35_000;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct MemoryDatabaseOptions {
24 pub events: bool,
26 pub max_events: Option<usize>,
32}
33
34impl Default for MemoryDatabaseOptions {
35 fn default() -> Self {
36 Self {
37 events: false,
38 max_events: Some(MAX_EVENTS),
39 }
40 }
41}
42
43impl MemoryDatabaseOptions {
44 pub fn new() -> Self {
46 Self::default()
47 }
48}
49
50#[derive(Debug, Clone)]
51enum InnerMemoryDatabase {
52 Tracker(Arc<RwLock<LruCache<EventId, ()>>>),
54 Full(DatabaseHelper),
56}
57
58#[derive(Debug, Clone)]
60pub struct MemoryDatabase {
61 inner: InnerMemoryDatabase,
62}
63
64impl Default for MemoryDatabase {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl MemoryDatabase {
71 pub fn new() -> Self {
73 Self::with_opts(MemoryDatabaseOptions::default())
74 }
75
76 pub fn with_opts(mut opts: MemoryDatabaseOptions) -> Self {
78 if let Some(0) = opts.max_events {
80 opts.max_events = Some(MAX_EVENTS);
81 }
82
83 let inner: InnerMemoryDatabase = if opts.events {
85 let helper: DatabaseHelper = match opts.max_events {
86 Some(max) => DatabaseHelper::bounded(max),
87 None => DatabaseHelper::unbounded(),
88 };
89 InnerMemoryDatabase::Full(helper)
90 } else {
91 let cache: LruCache<EventId, ()> = match opts.max_events {
92 Some(max) if max > 0 => {
93 let max: NonZeroUsize = NonZeroUsize::new(max).unwrap();
95 LruCache::new(max)
96 }
97 _ => LruCache::unbounded(),
98 };
99 InnerMemoryDatabase::Tracker(Arc::new(RwLock::new(cache)))
100 };
101
102 Self { inner }
103 }
104}
105
106impl NostrDatabase for MemoryDatabase {
107 fn backend(&self) -> Backend {
108 Backend::Memory
109 }
110
111 fn save_event<'a>(
112 &'a self,
113 event: &'a Event,
114 ) -> BoxedFuture<'a, Result<SaveEventStatus, DatabaseError>> {
115 Box::pin(async move {
116 match &self.inner {
117 InnerMemoryDatabase::Tracker(tracker) => {
118 let mut seen_event_ids = tracker.write().await;
120 seen_event_ids.put(event.id, ());
121
122 Ok(SaveEventStatus::Success)
123 }
124 InnerMemoryDatabase::Full(helper) => {
125 let DatabaseEventResult { status, .. } = helper.index_event(event).await;
126 Ok(status)
127 }
128 }
129 })
130 }
131
132 fn check_id<'a>(
133 &'a self,
134 event_id: &'a EventId,
135 ) -> BoxedFuture<'a, Result<DatabaseEventStatus, DatabaseError>> {
136 Box::pin(async move {
137 match &self.inner {
138 InnerMemoryDatabase::Tracker(tracker) => {
139 let seen_event_ids = tracker.read().await;
140
141 Ok(if seen_event_ids.contains(event_id) {
142 DatabaseEventStatus::Saved
143 } else {
144 DatabaseEventStatus::NotExistent
145 })
146 }
147 InnerMemoryDatabase::Full(helper) => {
148 if helper.has_event_id_been_deleted(event_id).await {
149 Ok(DatabaseEventStatus::Deleted)
150 } else if helper.has_event(event_id).await {
151 Ok(DatabaseEventStatus::Saved)
152 } else {
153 Ok(DatabaseEventStatus::NotExistent)
154 }
155 }
156 }
157 })
158 }
159
160 fn event_by_id<'a>(
161 &'a self,
162 event_id: &'a EventId,
163 ) -> BoxedFuture<'a, Result<Option<Event>, DatabaseError>> {
164 Box::pin(async move {
165 match &self.inner {
166 InnerMemoryDatabase::Tracker(..) => Ok(None),
167 InnerMemoryDatabase::Full(helper) => Ok(helper.event_by_id(event_id).await),
168 }
169 })
170 }
171
172 fn count(&self, filter: Filter) -> BoxedFuture<Result<usize, DatabaseError>> {
173 Box::pin(async move {
174 match &self.inner {
175 InnerMemoryDatabase::Tracker(..) => Ok(0),
176 InnerMemoryDatabase::Full(helper) => Ok(helper.count(filter).await),
177 }
178 })
179 }
180
181 fn query(&self, filter: Filter) -> BoxedFuture<Result<Events, DatabaseError>> {
182 Box::pin(async move {
183 match &self.inner {
184 InnerMemoryDatabase::Tracker(..) => Ok(Events::new(&filter)),
185 InnerMemoryDatabase::Full(helper) => Ok(helper.query(filter).await),
186 }
187 })
188 }
189
190 fn negentropy_items(
191 &self,
192 filter: Filter,
193 ) -> BoxedFuture<Result<Vec<(EventId, Timestamp)>, DatabaseError>> {
194 Box::pin(async move {
195 match &self.inner {
196 InnerMemoryDatabase::Tracker(..) => Ok(Vec::new()),
197 InnerMemoryDatabase::Full(helper) => Ok(helper.negentropy_items(filter).await),
198 }
199 })
200 }
201
202 fn delete(&self, filter: Filter) -> BoxedFuture<Result<(), DatabaseError>> {
203 Box::pin(async move {
204 match &self.inner {
205 InnerMemoryDatabase::Tracker(..) => Ok(()),
206 InnerMemoryDatabase::Full(helper) => {
207 helper.delete(filter).await;
208 Ok(())
209 }
210 }
211 })
212 }
213
214 fn wipe(&self) -> BoxedFuture<Result<(), DatabaseError>> {
215 Box::pin(async move {
216 match &self.inner {
217 InnerMemoryDatabase::Tracker(tracker) => {
218 let mut seen_event_ids = tracker.write().await;
219 seen_event_ids.clear();
220 }
221 InnerMemoryDatabase::Full(helper) => {
222 helper.clear().await;
223 }
224 }
225
226 Ok(())
227 })
228 }
229}