aboutsummaryrefslogtreecommitdiff
path: root/database/src/table.rs
blob: e2f6196220267c016cf8eec6e9a4bcaf526002a6 (plain)
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
/*
    This file is part of jellything (https://codeberg.org/metamuffin/jellything)
    which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
    Copyright (C) 2025 metamuffin <metamuffin.org>
*/

use crate::{
    backends::{ReadTransaction, WriteTransaction},
    indices::Index,
};
use anyhow::Result;
use serde::{Serialize, de::DeserializeOwned};

pub type RowNum = u64;

pub struct Table<T> {
    id: u32,
    pub(crate) indices: Vec<Box<dyn Index<T>>>,
}
impl<T: Serialize + DeserializeOwned> Table<T> {
    pub fn new(id: u32) -> Self {
        Self {
            id,
            indices: Vec::new(),
        }
    }
    fn key(&self, row: RowNum) -> Vec<u8> {
        let mut key = Vec::new();
        key.extend(self.id.to_be_bytes());
        key.extend(row.to_be_bytes());
        key
    }
    pub fn insert(&self, db: &mut dyn WriteTransaction, entry: T) -> Result<RowNum> {
        let mut id_counter = db
            .get(&self.id.to_be_bytes())?
            .map(|k| k.as_slice().try_into().map(RowNum::from_be_bytes).ok())
            .flatten()
            .unwrap_or(0);
        let row = id_counter;
        id_counter += 1;
        db.set(&self.id.to_be_bytes(), &id_counter.to_be_bytes())?;

        db.set(&self.key(row), &serde_json::to_vec(&entry)?)?;

        for idx in &self.indices {
            idx.add(db, row, &entry)?;
        }

        Ok(id_counter)
    }
    pub fn get(&self, db: &dyn ReadTransaction, row: RowNum) -> Result<Option<T>> {
        Ok(db
            .get(&self.key(row))?
            .map(|v| serde_json::from_slice(&v))
            .transpose()?)
    }
    pub fn remove(&self, db: &mut dyn WriteTransaction, row: RowNum) -> Result<bool> {
        let Some(entry) = self.get(db, row)? else {
            return Ok(false);
        };
        for idx in &self.indices {
            idx.remove(db, row, &entry)?;
        }
        db.del(&self.key(row))?;
        Ok(true)
    }
}