1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// Copyright © 2025 Stephan Kunz
//! Implementation of the [`Database`].
use core::any::Any;
use alloc::{collections::btree_map::BTreeMap, sync::Arc};
use spin::RwLock;
use crate::{
ConstString, Error,
entry::{EntryData, EntryPtr, EntryReadGuard, EntryWriteGuard},
error::Result,
};
/// Holds all [`Databoard`](crate::databoard::Databoard) data.
#[derive(Default)]
pub struct Database {
storage: BTreeMap<ConstString, EntryPtr>,
}
impl core::fmt::Debug for Database {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Entries {{ [")?;
let mut comma = false;
for entry in &self.storage {
if comma {
write!(f, ", ")?;
} else {
comma = true;
}
write!(f, "(key: {}", entry.0)?;
let data = entry.1.read();
write!(f, ", sequence_id: {:?}", data.sequence_id)?;
let value = data.data().as_ref();
write!(f, ", value: {value:?})")?;
}
write!(f, "] }}")
}
}
impl Database {
/// Returns `true` if a certain `key` is available, otherwise `false`.
#[must_use]
pub fn contains_key(&self, key: &str) -> bool {
self.storage.contains_key(key)
}
/// Returns a result of `true` if a certain `key` of type `T` is available, otherwise a result of `false`.
/// # Errors
/// - [`Error::WrongType`] if the entry has not the expected type `T`.
pub fn contains<T: Any + Send + Sync>(&self, key: &str) -> Result<bool> {
if let Some(entry) = self.storage.get(key) {
let en = &*entry.read().data;
if en.downcast_ref::<T>().is_none() {
return Err(Error::WrongType { key: key.into() });
}
return Ok(true);
}
Ok(false)
}
/// Creates a value of type `T` under `key`.
/// # Errors
/// - [`Error::AlreadyExists`] if `key` already exists.
pub fn create<T: Any + Send + Sync>(&mut self, key: impl Into<ConstString>, value: T) -> Result<()> {
let key = key.into();
if self.storage.contains_key(&key) {
return Err(Error::AlreadyExists { key });
}
let entry = Arc::new(RwLock::new(EntryData::new(value)));
if self.storage.insert(key, entry).is_some() {
return Err(Error::Unreachable(file!().into(), line!()));
}
Ok(())
}
/// Returns the value of type `T` stored under `key` and deletes it from storage.
/// # Errors
/// - [`Error::NotFound`] if `key` is not contained.
/// - [`Error::WrongType`] if the entry has not the expected type `T`.
pub fn delete<T: Any + Send + Sync>(&mut self, key: &str) -> Result<T> {
// check type
if let Some(entry) = self.storage.get(key) {
if entry.read().data.downcast_ref::<T>().is_none() {
return Err(Error::WrongType { key: key.into() });
}
} else {
return Err(Error::NotFound { key: key.into() });
}
if let Some(old) = self.storage.remove(key)
&& let Some(entry) = Arc::into_inner(old)
{
let entry_data = entry.into_inner(); // will block, if the RwLock is locked
match entry_data.data.downcast::<T>() {
Ok(t) => return Ok(*t),
Err(_) => return Err(Error::WrongType { key: key.into() }),
}
}
// We should never reach this!
Err(Error::Unreachable(file!().into(), line!()))
}
/// Returns a clone of the [`EntryPtr`]
/// # Errors
/// - [`Error::NotFound`] if `key` is not contained.
pub fn entry(&self, key: &str) -> Result<EntryPtr> {
if let Some(entry) = self.storage.get(key) {
return Ok(entry.clone());
}
Err(Error::NotFound { key: key.into() })
}
/// Returns a read/write guard to the `T` of the `entry` stored under `key`.
/// The entry is locked for read & write while this reference is held.
/// Multiple changes during holding the reference are counted as a single change,
/// so `sequence_id()`will only increase by 1.
///
/// You need to drop the received [`EntryWriteGuard`] before using `delete`, `read`, `update` or `sequence_id`.
/// # Errors
/// - [`Error::NotFound`] if `key` is not contained.
/// - [`Error::WrongType`] if the entry has not the expected type `T`.
pub fn get_mut_ref<T: Any + Send + Sync>(&self, key: &str) -> Result<EntryWriteGuard<T>> {
if let Some(entry) = self.storage.get(key) {
return EntryWriteGuard::new(key, entry);
}
Err(Error::NotFound { key: key.into() })
}
/// Returns a read guard to the `T` of the `entry` stored under `key`.
/// The entry is locked for write while this reference is held.
///
/// You need to drop the received [`EntryReadGuard`] before using `delete`, or `update`.
/// # Errors
/// - [`Error::NotFound`] if `key` is not contained.
/// - [`Error::WrongType`] if the entry has not the expected type `T`.
pub fn get_ref<T: Any + Send + Sync>(&self, key: &str) -> Result<EntryReadGuard<T>> {
if let Some(entry) = self.storage.get(key) {
return EntryReadGuard::new(key, entry.clone());
}
Err(Error::NotFound { key: key.into() })
}
/// Returns a copy of the value of type `T` stored under `key`.
/// # Errors
/// - [`Error::NotFound`] if `key` is not contained.
/// - [`Error::WrongType`] if the entry has not the expected type `T`.
pub fn read<T: Any + Clone + Send + Sync>(&self, key: &str) -> Result<T> {
self.storage.get(key).map_or_else(
|| Err(Error::NotFound { key: key.into() }),
|entry| {
let en = &*entry.read().data;
let t = en.downcast_ref::<T>();
t.cloned()
.map_or_else(|| Err(Error::WrongType { key: key.into() }), |v| Ok(v))
},
)
}
/// Returns the sequence id of an entry.
/// The sequence id starts with '1' and is increased at every change of an entry.
/// The sequence wraps around to '1' after reaching [`usize::MAX`] .
/// # Errors
/// - [`Error::NotFound`] if `key` is not contained.
pub fn sequence_id(&self, key: &str) -> Result<usize> {
self.storage.get(key).map_or_else(
|| Err(Error::NotFound { key: key.into() }),
|entry| Ok(entry.read().sequence_id),
)
}
/// Returns a read/write guard to the `T` of the `entry` stored under `key`.
/// The entry is locked for read & write while this reference is held.
/// Multiple changes during holding the reference are counted as a single change,
/// so `sequence_id()`will only increase by 1.
///
/// You need to drop the received [`EntryWriteGuard`] before using `delete`, `read`, `update` or `sequence_id`.
/// # Errors
/// - [`Error::IsLocked`] if the entry is locked by someone else.
/// - [`Error::NotFound`] if `key` is not contained.
/// - [`Error::WrongType`] if the entry has not the expected type `T`.
pub fn try_get_mut_ref<T: Any + Send + Sync>(&self, key: &str) -> Result<EntryWriteGuard<T>> {
if let Some(entry) = self.storage.get(key) {
return EntryWriteGuard::try_new(key, entry);
}
Err(Error::NotFound { key: key.into() })
}
/// Returns a read guard to the `T` of the `entry` stored under `key`.
/// The entry is locked for write while this reference is held.
///
/// You need to drop the received [`EntryReadGuard`] before using `delete`, or `update`.
/// # Errors
/// - [`Error::IsLocked`] if the entry is write locked by someone else.
/// - [`Error::NotFound`] if `key` is not contained.
/// - [`Error::WrongType`] if the entry has not the expected type `T`.
pub fn try_get_ref<T: Any + Send + Sync>(&self, key: &str) -> Result<EntryReadGuard<T>> {
if let Some(entry) = self.storage.get(key) {
return EntryReadGuard::try_new(key, entry);
}
Err(Error::NotFound { key: key.into() })
}
/// Updates a value of type `T` stored under `key` and returns the old value.
/// # Errors
/// - [`Error::NotFound`] if `key` is not contained.
/// - [`Error::WrongType`] if the entry has not the expected type `T`.
pub fn update<T: Any + Send + Sync>(&self, key: &str, value: T) -> Result<T> {
let mut value = value;
self.storage.get(key).map_or_else(
|| Err(Error::NotFound { key: key.into() }),
|entry| {
let en = &mut *entry.write();
if let Some(t) = en.data.downcast_mut::<T>() {
core::mem::swap(t, &mut value);
if en.sequence_id < usize::MAX {
en.sequence_id += 1;
} else {
en.sequence_id = 1;
}
Ok(value)
} else {
Err(Error::WrongType { key: key.into() })
}
},
)
}
}
#[cfg(test)]
mod tests {
use super::*;
// check, that the auto traits are available
const fn is_normal<T: Sized + Send + Sync>() {}
#[test]
const fn normal_types() {
is_normal::<Database>();
}
}