Skip to main content

somedb/
db.rs

1use std::{
2    collections::HashMap,
3    error::Error,
4    fs::{self},
5    path::PathBuf,
6    sync::Mutex,
7};
8
9use crate::{
10    byte_reader::ByteReader,
11    entity::Entity,
12    entity_meta::EntityMeta,
13    id::IdType,
14    query::{DbQuery, DbQueryMut},
15    storable::Storable,
16    type_hash::TypeHash,
17};
18
19static DATABASE_CREATED: Mutex<bool> = Mutex::new(false);
20
21/// A SomeDb instance
22#[derive(Debug)]
23pub struct Database {
24    db_dir: PathBuf,
25    stored_types: HashMap<TypeHash, ()>,
26}
27
28impl Database {
29    pub fn default(clear: bool) -> DbResult<Self> {
30        Self::new(PathBuf::from("sdb/"), clear)
31    }
32
33    pub fn new(db_dir: PathBuf, clear: bool) -> DbResult<Self> {
34        // a little bit hackey but the best we'll do for now
35        let mut db_created = DATABASE_CREATED.lock().unwrap();
36        if *db_created {
37            return Err(DbError::DbInstanceExists);
38        }
39        *db_created = true;
40
41        if clear {
42            let _ = fs::remove_dir_all(&db_dir);
43        }
44
45        let _ = fs::create_dir_all(&db_dir);
46
47        let stored_types: HashMap<_, _> = fs::read_dir(&db_dir)?
48            .filter_map(|f| {
49                let name = f.as_ref().ok()?.file_name().into_string().ok()?;
50                let parts: Vec<_> = name.split('.').collect();
51                if parts.len() > 2 || parts[1] != "sdb" {
52                    return None;
53                }
54
55                let type_hash = TypeHash::decode(parts[0]);
56
57                let mut opts = fs::OpenOptions::new();
58                opts.read(true);
59                opts.write(true);
60                opts.open(f.ok()?.path()).ok()?;
61
62                return Some((type_hash, ()));
63            })
64            .collect();
65
66        Ok(Database {
67            db_dir,
68            stored_types,
69        })
70    }
71
72    pub fn store<T: Entity>(&mut self, mut data: T) -> DbResult<T> {
73        let type_hash = T::type_hash();
74
75        if !self.stored_types.contains_key(&type_hash) {
76            self.add_new_type::<T>()?;
77        }
78
79        let mut existing = self.raw_read_all::<T>()?;
80
81        if !T::GENERATE_ID
82            && existing
83                .entities
84                .iter()
85                .find(|e| e.get_id() == data.get_id())
86                .is_some()
87        {
88            return Err(DbError::IdExists);
89        }
90
91        if T::GENERATE_ID {
92            data.set_id(<T::Id as IdType>::generate(existing.last_id))
93        }
94
95        existing.entities.push(data.clone());
96        existing.last_id = data.get_id();
97
98        self.raw_write_all(existing)?;
99
100        Ok(data)
101    }
102
103    pub fn write_all<T: Entity>(&mut self, entities: Vec<T>) -> DbResult<()> {
104        let type_hash = T::type_hash();
105
106        if !self.stored_types.contains_key(&type_hash) {
107            self.add_new_type::<T>()?;
108        }
109
110        let last_id = entities
111            .last()
112            .map(|e| e.get_id())
113            .unwrap_or_else(|| <T::Id as IdType>::initial());
114
115        self.raw_write_all(EntityMeta { last_id, entities })?;
116
117        Ok(())
118    }
119
120    pub fn raw_write_all<T: Entity>(&mut self, raw: EntityMeta<T>) -> DbResult<()> {
121        let type_hash = T::type_hash();
122
123        let new_data = raw.encoded();
124
125        fs::write(self.type_hash_file_path(&type_hash), new_data)?;
126
127        Ok(())
128    }
129
130    pub fn read_all<T: Entity>(&self) -> DbResult<Vec<T>> {
131        let type_hash = T::type_hash();
132
133        self.stored_types
134            .get(&type_hash)
135            .ok_or(DbError::TypeNotFound)?;
136
137        Ok(self.raw_read_all()?.entities)
138    }
139
140    pub fn raw_read_all<T: Entity>(&self) -> DbResult<EntityMeta<T>> {
141        let type_hash = T::type_hash();
142
143        let vec = fs::read(self.type_hash_file_path(&type_hash))?;
144
145        let mut reader = ByteReader::new(&vec);
146
147        EntityMeta::decoded(reader.reader_for_block())
148    }
149
150    pub fn read_all_ids<T: Entity>(&self) -> DbResult<Vec<T::Id>> {
151        Ok(self.read_all::<T>()?.iter().map(|e| e.get_id()).collect())
152    }
153
154    pub fn find_by_id<T: Entity>(&self, id: T::Id) -> DbResult<Option<T>> {
155        Ok(self.read_all::<T>()?.into_iter().find(|e| e.get_id() == id))
156    }
157
158    pub fn update_entity<T: Entity>(&mut self, entity: T) -> DbResult<()> {
159        let type_hash = T::type_hash();
160
161        self.stored_types
162            .get(&type_hash)
163            .ok_or(DbError::TypeNotFound)?;
164
165        let mut raw = self.raw_read_all::<T>()?;
166        let res = raw
167            .entities
168            .iter_mut()
169            .find(|e| e.get_id() == entity.get_id())
170            .ok_or(DbError::IdNotFound)?;
171
172        *res = entity;
173
174        self.raw_write_all(raw)?;
175
176        Ok(())
177    }
178
179    pub fn delte_entity_by_id<T: Entity>(&mut self, id: T::Id) -> DbResult<()> {
180        let type_hash = T::type_hash();
181
182        self.stored_types
183            .get(&type_hash)
184            .ok_or(DbError::TypeNotFound)?;
185
186        let mut raw = self.raw_read_all::<T>()?;
187        raw.entities = raw
188            .entities
189            .into_iter()
190            .filter(|e| e.get_id() != id)
191            .collect();
192
193        self.raw_write_all(raw)?;
194        Ok(())
195    }
196
197    pub fn delete_entity_store<T: Entity>(&mut self) -> DbResult<()> {
198        let type_hash = T::type_hash();
199        self.stored_types
200            .remove(&type_hash)
201            .ok_or(DbError::TypeNotFound)?;
202        fs::remove_file(self.type_hash_file_path(&type_hash))?;
203        Ok(())
204    }
205
206    fn add_new_type<T: Entity>(&mut self) -> DbResult<()> {
207        let type_hash = T::type_hash();
208
209        let mut opts = fs::OpenOptions::new();
210        opts.read(true);
211        opts.write(true);
212        opts.create(true);
213        opts.open(self.type_hash_file_path(&type_hash))?;
214
215        self.raw_write_all::<T>(EntityMeta {
216            last_id: <T::Id as IdType>::initial(),
217            entities: vec![],
218        })?;
219
220        self.stored_types.insert(type_hash, ());
221        Ok(())
222    }
223
224    fn type_hash_file_path(&self, type_hash: &TypeHash) -> PathBuf {
225        let mut path = self.db_dir.clone();
226        path.push(PathBuf::from(format!("{}.sdb", type_hash.encode())));
227        path
228    }
229
230    /// Creates a [DbQuery](crate::query::DbQuery) which can
231    /// be used to query the database like any other iterator.
232    pub fn query<T: Entity>(&self) -> DbResult<DbQuery<T>> {
233        DbQuery::new(self)
234    }
235
236    /// Creates a [DbQueryMut](crate::query::DbQueryMut) which
237    /// can be used to query the database and make changes to it
238    /// which can then be applied to the database via the
239    /// [save_to_db](crate::query::DbIterator::save_to_db) function.
240    pub fn query_mut<T: 'static + Entity>(&mut self) -> DbResult<DbQueryMut<T>> {
241        DbQueryMut::new(self)
242    }
243}
244
245impl Drop for Database {
246    fn drop(&mut self) {
247        *DATABASE_CREATED.lock().unwrap() = false;
248    }
249}
250
251pub type DbResult<T> = Result<T, DbError>;
252
253#[derive(Debug)]
254pub enum DbError {
255    IdExists,
256    TypeNotFound,
257    IdNotFound,
258    IoError(std::io::Error),
259    LoadError,
260    DbInstanceExists,
261    InvalidFileVersion,
262}
263
264impl PartialEq for DbError {
265    fn eq(&self, other: &Self) -> bool {
266        match self {
267            Self::IdExists => match other {
268                Self::IdExists => true,
269                _ => false,
270            },
271            Self::TypeNotFound => match other {
272                Self::TypeNotFound => true,
273                _ => false,
274            },
275            Self::IdNotFound => match other {
276                Self::IdNotFound => true,
277                _ => false,
278            },
279            Self::IoError(_) => false,
280            Self::LoadError => match other {
281                Self::LoadError => true,
282                _ => false,
283            },
284            Self::DbInstanceExists => match other {
285                Self::DbInstanceExists => true,
286                _ => false,
287            },
288            Self::InvalidFileVersion => match other {
289                Self::InvalidFileVersion => true,
290                _ => false,
291            },
292        }
293    }
294}
295
296impl From<std::io::Error> for DbError {
297    fn from(value: std::io::Error) -> Self {
298        Self::IoError(value)
299    }
300}
301
302impl std::fmt::Display for DbError {
303    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304        write!(f, "{self:?}")
305    }
306}
307
308impl Error for DbError {}