Skip to content

Commit

Permalink
Merge pull request ankurah#1 from synestheticsystems/model
Browse files Browse the repository at this point in the history
Split Record into Record/ScopedRecord
  • Loading branch information
Aceeri authored Nov 15, 2024
2 parents 055d5a2 + da108d3 commit f1bd61a
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 205 deletions.
49 changes: 35 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ derive = ["ankurah-derive"]

[dependencies]
anyhow = "1.0.89"
thiserror = "2"
chrono = "0.4.38"
dirs = "5.0.1"
futures-signals = "0.3.34"
Expand Down
42 changes: 15 additions & 27 deletions core/examples/example1.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use std::sync::Arc;

use ankurah_core::model::Record;
use ankurah_core::property::value::StringValue;
use ankurah_core::property::value::YrsString;
use ankurah_core::storage::SledStorageEngine;
use ankurah_core::{model::ID, node::Node};
use ankurah_core::{model::{ID, Record, ScopedRecord}, node::Node};
use ankurah_derive::Model;
use anyhow::Result;
use serde::{Deserialize, Serialize};
Expand All @@ -16,7 +15,8 @@ pub struct Album {
// Implication: we will still need to have a native type to active type lookup in the Model macro for now.
// We have the option of adding override attributes to switch backends in the future.
// We will initially only use Model structs for initial construction of the record (or a property group thereof) but we may later consider
// using them for per-propertygroup retrieival binding, but preferably only via an immutable borrow.
// using them for per-property group retrieval binding, but preferably only via an immutable borrow.
#[active_value(YrsString)]
name: String,
}

Expand All @@ -42,13 +42,12 @@ async fn main() -> Result<()> {
// let w : WritableRecord = album2.edit(trx);
// w.status.set("new name");
// })


// POINT 4 - explicit transactions for write/read? (how does this affect subscribers?)
// POINT 5 - trx record vs UOW registration?
// POINT 6 - backend instantiation and setup - who has a copy of what and how is it instantiated from a wayfinding perspective?
// POINT 7 - State and Event DAG construction and happens before determination (operation id/precursor in state and event data)

// Gradually uncomment this example as we add functionality
// let server = Node::new();
let mut client = Node::new(Box::new(SledStorageEngine::new().unwrap()));
Expand Down Expand Up @@ -86,9 +85,8 @@ async fn main() -> Result<()> {
// name: "The Dark Sid of the Moon".to_string(),
// }).insert();


// Conducive to macro syntax like create_album! { name: "", other: "" }
// AND conducive to property graph usage in the future
// AND conducive to property graph usage in the future
// let record = AlbumRecord::build().add<StringValue>("name","The Dark Sid of the Moon").add<StringValue>("other", "whatever").insert(&client);
// record.name()

Expand All @@ -104,32 +102,22 @@ async fn main() -> Result<()> {
// * Decouples models from records a little bit more (which is a good thing for property graph future stuff)
// * allows for individual field construction in the event that we want to direclyt construct value objects without injecting the record inner after the fact
// Downsides:
// *
// *

info!("Album created: {:?}", album);
album.name().insert(12, "e");
client.begin();
let scoped_album = album.edit(&trx).unwrap();
scoped_album.name().insert(12, "e");
use ankurah_core::property::traits::StateSync;
let update = scoped_album.name().get_pending_update();
println!("Update length: {}", update.unwrap().len());
assert_eq!(scoped_album.name().value(), "The Dark Side of the Moon");
trx.commit().unwrap();

client
.bucket("album")
.set_state(album.id(), album.record_state())?;
{
// info!("ababa");
let trx = client.begin();
let test = trx.get::<AlbumRecord>("album", album.id())?;
assert_eq!(test.name().value(), "The Dark Side of the Moon");
println!("test: {:?}", test.name().value());
// info!("ababa");
}

album
};

assert_eq!(album.name.value(), "The Dark Side of the Moon");

use ankurah_core::property::traits::StateSync;
let update = album.name().get_pending_update();
println!("Update length: {}", update.unwrap().len());
assert_eq!(album.name(), "The Dark Side of the Moon");

// should immediately have two operations - one for the initial insert, and one for the edit
// assert_eq!(album.operation_count(), 2);
Expand Down
15 changes: 7 additions & 8 deletions core/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
use crate::model::ID;
use thiserror::Error;

#[derive(Debug)]
#[derive(Error, Debug)]
pub enum RetrievalError {
#[error("ID {0:?} not found")]
NotFound(ID),
#[error("Storage error: {0}")]
StorageError(Box<dyn std::error::Error>),
#[error("Update failed: {0}")]
FailedUpdate(Box<dyn std::error::Error>),
#[error("Deserialization error: {0}")]
DeserializationError(bincode::Error),
}

impl From<bincode::Error> for RetrievalError {
fn from(e: bincode::Error) -> Self {
RetrievalError::DeserializationError(e)
}
}

impl From<RetrievalError> for anyhow::Error {
fn from(e: RetrievalError) -> Self {
anyhow::anyhow!("{:?}", e)
}
}
}
41 changes: 27 additions & 14 deletions core/src/model.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// use futures_signals::signal::Signal;

use std::{any::Any, sync::Arc};
use std::{any::Any, sync::Arc, fmt};

use crate::{storage::RecordState, Node};
use crate::{error::RetrievalError, storage::RecordState, Node};

use anyhow::Result;

Expand All @@ -11,28 +11,41 @@ use ulid::Ulid;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Ord, PartialOrd)]
pub struct ID(pub Ulid);

impl fmt::Display for ID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.0.fmt(f)
}
}

/// A model is a struct that represents the present values for a given record
/// Schema is defined primarily by the Model object, and the Record is derived from that via macro.
pub trait Model {}
pub trait Model {
type Record: Record;
type ScopedRecord: ScopedRecord;
fn bucket_name() -> &'static str
where
Self: Sized;
}

/// A specific instance of a record in the collection
pub trait Record: Any + Send + Sync + 'static {
/// An instance of a record.
pub trait Record {
fn id(&self) -> ID;
fn bucket_name() -> &'static str
where
Self: Sized;
}

/// An editable instance of a record.
pub trait ScopedRecord: Any + Send + Sync + 'static {
fn id(&self) -> ID;
fn bucket_name(&self) -> &'static str;
fn record_state(&self) -> RecordState;
fn from_record_state(
id: ID,
record_state: &RecordState,
) -> Result<Self, crate::error::RetrievalError>
) -> Result<Self, RetrievalError>
where
Self: Sized;
fn commit_record(&self, node: Arc<Node>) -> Result<()>;

fn as_dyn_any(&self) -> &dyn Any;
}

#[derive(Debug)]
pub struct RecordInner {
pub collection: &'static str,
pub id: ID,
}
}
3 changes: 1 addition & 2 deletions core/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ use ulid::Ulid;

use crate::{
event::Operation,
model::{Model, Record, ID},
model::{Record, ID},
storage::{Bucket, StorageEngine},
transaction::Transaction,
};
use anyhow::Result;
use std::{
collections::BTreeMap,
sync::{mpsc, Arc, RwLock},
Expand Down
26 changes: 25 additions & 1 deletion core/src/property/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,31 @@ use std::sync::Arc;
pub mod yrs;
pub use yrs::YrsBackend;

/// Temporary structure for constructing fields.
use crate::{error::RetrievalError, storage::RecordState};

use anyhow::Result;

/// Holds the property backends inside of records.
#[derive(Debug, Clone)]
pub struct Backends {
// Probably should be an `Option` since not all records will use each backend?
// Otherwise we might want to upcast this into something like `BTreeMap<BackendIdentifier, Box<dyn PropertyBackend>>`.
pub yrs: Arc<YrsBackend>,
// extend this with any backends needed.
}

impl Backends {
pub fn new() -> Self {
let yrs = Arc::new(YrsBackend::new());
Self {
yrs,
}
}

pub fn from_state_buffers(record_state: &RecordState) -> Result<Self, RetrievalError> {
let yrs = Arc::new(YrsBackend::from_state_buffer(&record_state.yrs_state_buffer)?);
Ok(Self {
yrs,
})
}
}
Loading

0 comments on commit f1bd61a

Please sign in to comment.