pique/src/store.rs

176 lines
3.9 KiB
Rust

use std::{
collections::{BTreeMap, HashMap, HashSet},
path::Path,
sync::Arc,
};
use anyhow::Result;
use uuid::Uuid;
use crate::models::{Document, Project};
pub trait Filter = Fn(&Row) -> bool;
pub trait Map<T> = Fn(&Row) -> T;
pub enum Row {
Project(Box<Project>),
Document(Box<Document>),
//User(Box<User>),
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct RowId(Uuid);
// supports comparison operators
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct IndexId(String);
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct IndexValue(String);
pub enum Index {
BTree(BTreeMap<IndexValue, HashSet<RowId>>),
Unique(HashMap<IndexValue, RowId>),
}
pub struct Store {
rows: HashMap<RowId, Arc<Row>>,
indexes: HashMap<IndexId, Index>,
keyspace: fjall::Keyspace,
data_partition: fjall::PartitionHandle,
permissions_partition: fjall::PartitionHandle,
}
impl Store {
pub fn new<P: AsRef<Path>>(path: P) -> Result<Store> {
let rows = HashMap::new();
let indexes = HashMap::new();
let kv_config = fjall::Config::new(path)
.flush_workers(4)
.compaction_workers(4);
let keyspace = fjall::Keyspace::open(kv_config)?;
let data_partition =
keyspace.open_partition("data", fjall::PartitionCreateOptions::default())?;
let permissions_partition =
keyspace.open_partition("permissions", fjall::PartitionCreateOptions::default())?;
Ok(Store {
rows,
indexes,
keyspace,
data_partition,
permissions_partition,
})
}
pub fn set(&mut self, _item: &Row) -> Result<()> {
todo!()
}
/// Retrieves an item from the store by id. This is always an in-memory
/// operation and cannot fail.
pub fn get(&self, id: RowId) -> Option<Arc<Row>> {
self.rows.get(&id).cloned()
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Comparison {
index: IndexId,
value: IndexValue,
operator: ComparisonOperator,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum ComparisonOperator {
Eq,
Gt,
Gte,
Lt,
Lte,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum QueryOperation {
All,
Where(Comparison),
Limit(usize),
Offset(usize),
OrderBy(IndexId),
}
/// Describes a set of operations to query for data in the database. To read the
/// data, this gets transformed into a QuerySet, which contains the results.
pub struct Query {
operations: Vec<QueryOperation>,
}
impl Query {
pub fn new() -> Query {
Query {
operations: Vec::new(),
}
}
pub fn all(&mut self) -> &mut Self {
self.operations.push(QueryOperation::All);
self
}
/// Filters down the set of rows via comparison operators
pub fn restrict(
&mut self,
index: IndexId,
value: IndexValue,
operator: ComparisonOperator,
) -> &mut Self {
self.operations.push(QueryOperation::Where(Comparison {
index,
value,
operator,
}));
self
}
pub fn limit(&mut self, limit: usize) -> &mut Self {
self.operations.push(QueryOperation::Limit(limit));
self
}
pub fn offset(&mut self, offset: usize) -> &mut Self {
self.operations.push(QueryOperation::Offset(offset));
self
}
pub fn order_by(&mut self, index: IndexId) -> &mut Self {
self.operations.push(QueryOperation::OrderBy(index));
self
}
pub fn execute(&self) -> QuerySet {
todo!()
}
}
/// The results of a Query, this will contain the concrete Rows which have been
/// retrieved.
pub struct QuerySet {}
impl QuerySet {}
impl Iterator for QuerySet {
type Item = Row;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
// How I'd like to do queries:
//
// store.query().via(OnlyProjects).
//
//
//