pub use persy::ValueMode;
use record::Record;
use std::path::{Path, PathBuf};
use std::sync::Arc;
mod desc;
mod format;
use desc::{Description, InternalDescription};
mod filter_builder;
mod index;
mod structsy;
pub use crate::structsy::{RawIter, RawPrepare, RawTransaction};
use crate::structsy::{RecordIter, StructsyImpl};
mod id;
pub use crate::id::Ref;
mod error;
pub use crate::error::{SRes, StructsyError};
mod queries;
pub use crate::queries::{Operators, SnapshotQuery, StructsyIter, StructsyQuery, StructsyQueryTx};
mod transaction;
pub use crate::transaction::{OwnedSytx, Prepared, RefSytx, StructsyTx, Sytx};
use filter_builder::FilterBuilder;
pub mod internal;
pub use internal::{Persistent, PersistentEmbedded};
mod filter;
mod projection;
pub use filter::Filter;
mod actions;
pub mod record;
#[cfg(feature = "derive")]
pub mod derive {
pub use structsy_derive::{embedded_queries, queries, Persistent, PersistentEmbedded, Projection};
}
mod snapshot;
pub use snapshot::Snapshot;
#[derive(Clone)]
pub struct Structsy {
structsy_impl: Arc<StructsyImpl>,
}
pub struct StructsyConfig {
create: bool,
path: PathBuf,
}
impl StructsyConfig {
pub fn create(mut self, create: bool) -> StructsyConfig {
self.create = create;
self
}
}
impl<T: AsRef<Path>> From<T> for StructsyConfig {
fn from(path: T) -> StructsyConfig {
StructsyConfig {
create: true,
path: path.as_ref().to_path_buf(),
}
}
}
pub struct PrepareOpen {
structsy_impl: Arc<StructsyImpl>,
}
impl PrepareOpen {
pub fn migrate<S, D>(&self) -> SRes<()>
where
S: Persistent,
D: Persistent,
D: From<S>,
{
self.structsy_impl.migrate::<S, D>()
}
pub fn open(self) -> SRes<Structsy> {
Ok(Structsy {
structsy_impl: self.structsy_impl,
})
}
}
pub trait Fetch<T> {
#[deprecated]
fn into(self, structsy: &Structsy) -> StructsyIter<T>;
fn fetch(self, structsy: &Structsy) -> StructsyIter<T>;
#[deprecated]
fn into_tx(self, tx: &mut OwnedSytx) -> StructsyIter<T>;
fn fetch_tx(self, tx: &mut OwnedSytx) -> StructsyIter<T>;
fn fetch_snapshot(self, structsy: &Snapshot) -> StructsyIter<T>;
}
#[deprecated]
pub trait IntoResult<T>: Fetch<T> {}
impl Structsy {
pub fn config<C: AsRef<Path>>(path: C) -> StructsyConfig {
let mut c = StructsyConfig::from(path);
c.create = false;
c
}
pub fn prepare_open<C: Into<StructsyConfig>>(config: C) -> SRes<PrepareOpen> {
Ok(PrepareOpen {
structsy_impl: Arc::new(StructsyImpl::open(config.into())?),
})
}
pub fn open<C: Into<StructsyConfig>>(config: C) -> SRes<Structsy> {
Ok(Structsy {
structsy_impl: Arc::new(StructsyImpl::open(config.into())?),
})
}
pub fn memory() -> SRes<Structsy> {
Ok(Structsy {
structsy_impl: Arc::new(StructsyImpl::memory()?),
})
}
pub fn define<T: Persistent>(&self) -> SRes<bool> {
self.structsy_impl.define::<T>()
}
pub fn undefine<T: Persistent>(&self) -> SRes<()> {
self.structsy_impl.undefine::<T>()
}
pub fn begin(&self) -> SRes<OwnedSytx> {
self.structsy_impl.begin()
}
pub fn read<T: Persistent>(&self, sref: &Ref<T>) -> SRes<Option<T>> {
self.structsy_impl.read(sref)
}
pub fn scan<T: Persistent>(&self) -> SRes<RecordIter<T>> {
self.structsy_impl.scan::<T>()
}
#[deprecated]
pub fn commit(&self, tx: OwnedSytx) -> SRes<()> {
self.structsy_impl.commit(tx)
}
pub fn is_defined<T: Persistent>(&self) -> SRes<bool> {
self.structsy_impl.is_defined::<T>()
}
pub fn query<T: Persistent>(&self) -> StructsyQuery<T> {
StructsyQuery {
structsy: self.clone(),
builder: FilterBuilder::new(),
}
}
pub fn fetch<R: Fetch<T>, T>(&self, filter: R) -> StructsyIter<T> {
filter.fetch(self)
}
#[deprecated]
pub fn into_iter<R: Fetch<T>, T>(&self, filter: R) -> StructsyIter<T> {
filter.fetch(self)
}
pub fn list_defined(&self) -> SRes<impl std::iter::Iterator<Item = desc::Description>> {
self.structsy_impl.list_defined()
}
pub fn snapshot(&self) -> SRes<Snapshot> {
Ok(Snapshot {
structsy_impl: self.structsy_impl.clone(),
ps: self.structsy_impl.persy.snapshot()?,
})
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Order {
Asc,
Desc,
}
pub trait RawRead {
fn raw_scan(&self, ty_name: &str) -> SRes<RawIter>;
fn raw_read(&self, id: &str) -> SRes<Option<Record>>;
}
pub trait RawAccess: RawRead {
fn raw_scan(&self, ty_name: &str) -> SRes<RawIter> {
RawRead::raw_scan(self, ty_name)
}
fn raw_read(&self, id: &str) -> SRes<Option<Record>> {
RawRead::raw_read(self, id)
}
fn raw_define(&self, desc: Description) -> SRes<bool>;
fn raw_begin(&self) -> SRes<RawTransaction>;
}
#[cfg(test)]
mod test {
use super::{
internal::{Description, FieldDescription, Query, StructDescription},
Persistent, Ref, SRes, Structsy, StructsyTx, Sytx,
};
use persy::ValueMode;
use std::fs;
use std::io::{Read, Write};
#[derive(Debug, PartialEq)]
struct ToTest {
name: String,
length: u32,
}
impl Persistent for ToTest {
fn get_name() -> &'static str {
"ToTest"
}
fn get_description() -> Description {
let fields: [FieldDescription; 2] = [
FieldDescription::new::<String>(0, "name", Some(ValueMode::Cluster)),
FieldDescription::new::<u32>(1, "length", None),
];
Description::Struct(StructDescription::new("ToTest", &fields))
}
fn write(&self, write: &mut dyn Write) -> SRes<()> {
use super::PersistentEmbedded;
self.name.write(write)?;
self.length.write(write)?;
Ok(())
}
fn read(read: &mut dyn Read) -> SRes<Self>
where
Self: std::marker::Sized,
{
use super::internal::PersistentEmbedded;
Ok(ToTest {
name: String::read(read)?,
length: u32::read(read)?,
})
}
fn declare(tx: &mut dyn Sytx) -> SRes<()> {
use super::internal::declare_index;
declare_index::<String>(tx, "ToTest.name", ValueMode::Exclusive)?;
Ok(())
}
fn put_indexes(&self, tx: &mut dyn Sytx, id: &Ref<Self>) -> SRes<()> {
use super::internal::IndexableValue;
self.name.puts(tx, "ToTest", &["name"], id)?;
Ok(())
}
fn remove_indexes(&self, tx: &mut dyn Sytx, id: &Ref<Self>) -> SRes<()> {
use super::internal::IndexableValue;
self.name.removes(tx, "ToTest", &["name"], id)?;
Ok(())
}
}
trait ToTestQueries {
fn all(self) -> Self;
}
impl<Q: Query<ToTest>> ToTestQueries for Q {
fn all(mut self) -> Self {
let _builder = self.filter_builder();
self
}
}
#[test]
fn simple_basic_flow() {
let db = Structsy::open("one.db").expect("can open the database");
db.define::<ToTest>().expect("is define correctly");
let mut tx = db.begin().expect("can start a transaction");
let val = ToTest {
name: "one".to_string(),
length: 3,
};
let id = tx.insert(&val).expect("insert correctly");
let mut read = tx.read(&id).expect("read correctly").expect("this should be some");
assert_eq!(read.name, val.name);
assert_eq!(read.length, val.length);
read.name = "new".to_string();
tx.update(&id, &read).expect("updated correctly");
let mut count = 0;
let mut iter = tx.scan::<ToTest>().expect("scan works");
assert_eq!(iter.tx().read(&id).expect("transaction access works").is_some(), true);
for (sid, rec) in iter {
assert_eq!(rec.name, read.name);
assert_eq!(rec.length, val.length);
assert_eq!(sid, id);
count += 1;
}
assert_eq!(count, 1);
count = 0;
let mut iter = tx.scan::<ToTest>().expect("scan works");
while let Some((sid, rec, _tx)) = iter.next_tx() {
assert_eq!(rec.name, read.name);
assert_eq!(rec.length, val.length);
assert_eq!(sid, id);
count += 1;
}
assert_eq!(count, 1);
tx.commit().expect("tx committed correctly");
let read_persistent = db.read(&id).expect("read correctly").expect("this is some");
assert_eq!(read_persistent.name, read.name);
assert_eq!(read_persistent.length, val.length);
let mut count = 0;
for (sid, rec) in db.scan::<ToTest>().expect("scan works") {
assert_eq!(rec.name, read.name);
assert_eq!(rec.length, val.length);
assert_eq!(sid, id);
count += 1;
}
assert_eq!(count, 1);
fs::remove_file("one.db").expect("remove file works");
}
#[derive(Debug)]
struct Pers {}
impl Persistent for Pers {
fn get_name() -> &'static str {
"Pers"
}
fn get_description() -> Description {
Description::Struct(StructDescription::new("Pers", &Vec::new()))
}
fn write(&self, _write: &mut dyn Write) -> SRes<()> {
Ok(())
}
fn read(_read: &mut dyn Read) -> SRes<Self>
where
Self: std::marker::Sized,
{
Ok(Pers {})
}
fn declare(_db: &mut dyn Sytx) -> SRes<()> {
Ok(())
}
fn put_indexes(&self, _tx: &mut dyn Sytx, _id: &Ref<Self>) -> SRes<()>
where
Self: std::marker::Sized,
{
Ok(())
}
fn remove_indexes(&self, _tx: &mut dyn Sytx, _id: &Ref<Self>) -> SRes<()>
where
Self: std::marker::Sized,
{
Ok(())
}
}
#[test]
pub fn test_id_display_parse() {
let id = Ref::<Pers>::new("s0c5a58".parse().unwrap());
let read: Ref<Pers> = format!("{}", &id).parse().unwrap();
assert_eq!(id, read);
}
}