move resolver to event
This commit is contained in:
parent
c3a185fe8b
commit
e208f62686
4 changed files with 196 additions and 92 deletions
24
src/event.rs
24
src/event.rs
|
@ -1,10 +1,12 @@
|
|||
use crate::atoms::EventId;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{atoms::EventId, resolver::Resolver};
|
||||
|
||||
#[derive(Debug)]
|
||||
/// An event, is the basic building block/envolope containing all data.
|
||||
pub struct Event<T> {
|
||||
pub struct Event<T, S> {
|
||||
pub id: EventId,
|
||||
pub content: CoreContent<T>,
|
||||
pub content: CoreContent<T, S>,
|
||||
pub references: Vec<EventId>,
|
||||
// pub author: ActorId,
|
||||
// pub signature: ActorSignature,
|
||||
|
@ -12,8 +14,8 @@ pub struct Event<T> {
|
|||
|
||||
#[derive(Debug)]
|
||||
/// This defines content of an event as either a builtin event or application-specific event.
|
||||
pub enum CoreContent<T> {
|
||||
Create(CreateContent),
|
||||
pub enum CoreContent<T, S> {
|
||||
Create(CreateContent<T, S>),
|
||||
Custom(T),
|
||||
|
||||
/*
|
||||
|
@ -41,12 +43,14 @@ pub enum CoreContent<T> {
|
|||
|
||||
#[derive(Debug)]
|
||||
/// Every room has exactly one immutable create event as the single root of the event dag.
|
||||
pub struct CreateContent {
|
||||
// TODO: move resolver stuff here
|
||||
pub struct CreateContent<T, S> {
|
||||
pub resolver: Box<dyn Resolver<Type = T, State = S>>,
|
||||
}
|
||||
|
||||
impl CreateContent {
|
||||
pub fn new() -> Self {
|
||||
Self { }
|
||||
impl<T, S> CreateContent<T, S> {
|
||||
pub fn new(resolver: Box<dyn Resolver<Type = T, State = S>>) -> Self {
|
||||
Self {
|
||||
resolver,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
139
src/main.rs
139
src/main.rs
|
@ -1,40 +1,145 @@
|
|||
#![allow(dead_code)] // TODO: remove this later!
|
||||
|
||||
use std::collections::HashMap;
|
||||
use event::Event;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{BTreeMap, HashMap},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::{event::CoreContent, resolver::Resolver, room::Room};
|
||||
|
||||
mod atoms;
|
||||
mod event;
|
||||
mod room;
|
||||
mod resolver;
|
||||
mod room;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum EventContent {
|
||||
/// A basic key value store
|
||||
enum KVEventContent {
|
||||
Set(String, String),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let resolver = Resolver::new(Box::new(|events| {
|
||||
// resolve/reduce state from a sorted list of events, "full resolution"
|
||||
#[derive(Debug)]
|
||||
/// A basic key filesystem-like thing
|
||||
enum FSEventContent {
|
||||
Put(PathBuf, Vec<u8>),
|
||||
Delete(PathBuf),
|
||||
}
|
||||
|
||||
struct KVResolver;
|
||||
struct FSResolver;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FSState {
|
||||
files: BTreeMap<PathBuf, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Resolver for KVResolver {
|
||||
type Type = KVEventContent;
|
||||
type State = HashMap<String, String>;
|
||||
|
||||
fn resolve(&self, events: &[&Event<Self::Type, Self::State>]) -> Self::State {
|
||||
dbg!(events);
|
||||
let mut kv = HashMap::new();
|
||||
for event in events {
|
||||
match &event.content {
|
||||
CoreContent::Create(_) => {},
|
||||
CoreContent::Custom(EventContent::Set(k, v)) => {
|
||||
CoreContent::Create(_) => {}
|
||||
CoreContent::Custom(KVEventContent::Set(k, v)) => {
|
||||
kv.insert(k.clone(), v.clone());
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
kv
|
||||
}), Box::new(|_a, _b| {
|
||||
// tiebreak two events
|
||||
todo!()
|
||||
}));
|
||||
let mut room = Room::new(resolver);
|
||||
room.create_event(CoreContent::Custom(EventContent::Set("foo".into(), "apple".into())));
|
||||
room.create_event(CoreContent::Custom(EventContent::Set("bar".into(), "banana".into())));
|
||||
room.create_event(CoreContent::Custom(EventContent::Set("baz".into(), "carrot".into())));
|
||||
}
|
||||
|
||||
fn tiebreak(
|
||||
&self,
|
||||
_a: &Event<Self::Type, Self::State>,
|
||||
_b: &Event<Self::Type, Self::State>,
|
||||
) -> Ordering {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolver for FSResolver {
|
||||
type Type = FSEventContent;
|
||||
type State = FSState;
|
||||
|
||||
fn resolve(&self, events: &[&Event<Self::Type, Self::State>]) -> Self::State {
|
||||
dbg!(events);
|
||||
let mut files = BTreeMap::new();
|
||||
for event in events {
|
||||
match &event.content {
|
||||
CoreContent::Create(_) => {}
|
||||
CoreContent::Custom(FSEventContent::Put(path, data)) => {
|
||||
files.insert(path.clone(), data.clone());
|
||||
}
|
||||
CoreContent::Custom(FSEventContent::Delete(match_path)) => {
|
||||
files.retain(|file_path, _| !file_path.starts_with(match_path))
|
||||
}
|
||||
}
|
||||
}
|
||||
FSState { files }
|
||||
}
|
||||
|
||||
fn tiebreak(
|
||||
&self,
|
||||
_a: &Event<Self::Type, Self::State>,
|
||||
_b: &Event<Self::Type, Self::State>,
|
||||
) -> Ordering {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl FSState {
|
||||
fn readstr(&self, path: &Path) -> Option<String> {
|
||||
self.files
|
||||
.get(path)
|
||||
.map(|b| String::from_utf8_lossy(b).to_string())
|
||||
}
|
||||
|
||||
fn list(&self, path: &Path) -> Vec<&Path> {
|
||||
self.files
|
||||
.keys()
|
||||
.filter(|file_path| file_path.starts_with(path))
|
||||
.map(|p| p.as_path())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut room = Room::new(Box::new(KVResolver));
|
||||
room.create_event(CoreContent::Custom(KVEventContent::Set(
|
||||
"foo".into(),
|
||||
"apple".into(),
|
||||
)));
|
||||
room.create_event(CoreContent::Custom(KVEventContent::Set(
|
||||
"bar".into(),
|
||||
"banana".into(),
|
||||
)));
|
||||
room.create_event(CoreContent::Custom(KVEventContent::Set(
|
||||
"baz".into(),
|
||||
"carrot".into(),
|
||||
)));
|
||||
dbg!(&room);
|
||||
dbg!(&room.get_state());
|
||||
|
||||
let mut room = Room::new(Box::new(FSResolver));
|
||||
room.create_event(CoreContent::Custom(FSEventContent::Put(
|
||||
"/readme.md".into(),
|
||||
b"Hello, world!".into(),
|
||||
)));
|
||||
room.create_event(CoreContent::Custom(FSEventContent::Put(
|
||||
"/remove/me.txt".into(),
|
||||
b"oops".into(),
|
||||
)));
|
||||
room.create_event(CoreContent::Custom(FSEventContent::Delete(
|
||||
"/remove".into(),
|
||||
)));
|
||||
let state = room.get_state();
|
||||
dbg!(&room);
|
||||
dbg!(&state);
|
||||
dbg!(&state.list(&Path::new("/")));
|
||||
dbg!(&state.readstr(&Path::new("/readme.md")));
|
||||
}
|
||||
|
|
|
@ -1,63 +1,54 @@
|
|||
use crate::{atoms::EventId, event::Event};
|
||||
use std::{cmp::Ordering, collections::HashSet, fmt::Debug, ops::Deref};
|
||||
use std::{cmp::Ordering, collections::HashSet, fmt::Debug};
|
||||
|
||||
pub type TiebreakFn<T> = Box<dyn Fn(&Event<T>, &Event<T>) -> Ordering>;
|
||||
pub type ResolveFn<T, S> = Box<dyn Fn(&[&Event<T>]) -> S>;
|
||||
|
||||
pub struct Resolver<T, S> {
|
||||
/// Given two events, decide which one comes first
|
||||
pub tiebreak_fn: TiebreakFn<T>,
|
||||
pub trait Resolver {
|
||||
type Type;
|
||||
type State;
|
||||
|
||||
/// Given a set of ordered events, resolve the final state
|
||||
pub resolve_fn: ResolveFn<T, S>,
|
||||
fn resolve(&self, events: &[&Event<Self::Type, Self::State>]) -> Self::State;
|
||||
|
||||
/// Given two events, decide which one comes first
|
||||
fn tiebreak(
|
||||
&self,
|
||||
a: &Event<Self::Type, Self::State>,
|
||||
b: &Event<Self::Type, Self::State>,
|
||||
) -> Ordering;
|
||||
}
|
||||
|
||||
impl<T: Debug, S> Resolver<T, S> {
|
||||
pub fn new(resolve_fn: ResolveFn<T, S>, tiebreak_fn: TiebreakFn<T>) -> Self {
|
||||
Resolver {
|
||||
tiebreak_fn,
|
||||
resolve_fn,
|
||||
}
|
||||
}
|
||||
|
||||
/// topologically sort a list of events
|
||||
pub fn sort<'a>(&'a self, events: &'a [Event<T>]) -> Vec<&Event<T>> {
|
||||
let mut references: HashSet<(EventId, EventId)> = events
|
||||
.iter()
|
||||
.flat_map(|event| {
|
||||
event
|
||||
.references
|
||||
.iter()
|
||||
.map(|parent_id| (parent_id.clone(), event.id.clone()))
|
||||
})
|
||||
.collect();
|
||||
let mut sorted = vec![];
|
||||
let (mut heads, mut unsorted): (Vec<_>, Vec<_>) =
|
||||
events.iter().partition(|event| event.references.is_empty());
|
||||
assert!(heads.len() == 1);
|
||||
while let Some(ev) = heads.pop() {
|
||||
references.retain(|(parent_id, _)| parent_id != &ev.id);
|
||||
sorted.push(ev);
|
||||
let (new_unsorted, mut children) = unsorted.into_iter().partition(|candidate| {
|
||||
references
|
||||
.iter()
|
||||
.any(|(_, child)| child == &candidate.id)
|
||||
});
|
||||
unsorted = new_unsorted;
|
||||
children.sort_by(|a, b| (self.tiebreak_fn)(a, b).then_with(|| a.id.cmp(&b.id)));
|
||||
heads.extend(children);
|
||||
}
|
||||
assert!(unsorted.is_empty());
|
||||
sorted
|
||||
}
|
||||
|
||||
/// resolve state from a set of unsorted events
|
||||
pub fn resolve(&self, events: &[Event<T>]) -> S {
|
||||
(self.resolve_fn)(self.sort(events).deref())
|
||||
/// topologically sort a list of events
|
||||
pub fn sort<T, S>(
|
||||
tiebreak: impl Fn(&Event<T, S>, &Event<T, S>) -> Ordering,
|
||||
events: &[Event<T, S>],
|
||||
) -> Vec<&Event<T, S>> {
|
||||
let mut references: HashSet<(EventId, EventId)> = events
|
||||
.iter()
|
||||
.flat_map(|event| {
|
||||
event
|
||||
.references
|
||||
.iter()
|
||||
.map(|parent_id| (parent_id.clone(), event.id.clone()))
|
||||
})
|
||||
.collect();
|
||||
let mut sorted = vec![];
|
||||
let (mut heads, mut unsorted): (Vec<_>, Vec<_>) =
|
||||
events.iter().partition(|event| event.references.is_empty());
|
||||
assert!(heads.len() == 1);
|
||||
while let Some(ev) = heads.pop() {
|
||||
references.retain(|(parent_id, _)| parent_id != &ev.id);
|
||||
sorted.push(ev);
|
||||
let (new_unsorted, mut children) = unsorted
|
||||
.into_iter()
|
||||
.partition(|candidate| references.iter().any(|(_, child)| child == &candidate.id));
|
||||
unsorted = new_unsorted;
|
||||
children.sort_by(|a, b| tiebreak(a, b).then_with(|| a.id.cmp(&b.id)));
|
||||
heads.extend(children);
|
||||
}
|
||||
assert!(unsorted.is_empty());
|
||||
sorted
|
||||
}
|
||||
|
||||
impl<T, S> Debug for Resolver<T, S> {
|
||||
impl<T, S> Debug for dyn Resolver<Type = T, State = S> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
|
|
32
src/room.rs
32
src/room.rs
|
@ -1,40 +1,44 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use crate::{atoms::EventId, event::{CoreContent, CreateContent, Event}, resolver::Resolver};
|
||||
use crate::{atoms::EventId, event::{CoreContent, CreateContent, Event}, resolver::{sort, Resolver}};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Room<E, S> {
|
||||
pub(super) resolver: Resolver<E, S>,
|
||||
pub(super) events: Vec<Event<E>>,
|
||||
pub(super) events: Vec<Event<E, S>>,
|
||||
pub(super) heads: Vec<EventId>,
|
||||
}
|
||||
|
||||
impl<T: Debug, S> Room<T, S> {
|
||||
pub fn new(resolver: Resolver<T, S>) -> Self {
|
||||
pub fn new(resolver: Box<dyn Resolver<Type = T, State = S>>) -> Self {
|
||||
let event_id = EventId::random();
|
||||
let base_event = Event {
|
||||
id: event_id.clone(),
|
||||
content: CoreContent::Create(CreateContent::new()),
|
||||
content: CoreContent::Create(CreateContent::new(resolver)),
|
||||
references: vec![],
|
||||
};
|
||||
Self {
|
||||
resolver,
|
||||
events: vec![base_event],
|
||||
heads: vec![event_id],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_root(&self) -> &Event<T> {
|
||||
pub fn get_root(&self) -> &Event<T, S> {
|
||||
self.events.first().as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_state(&mut self) -> S {
|
||||
// self.state_cache
|
||||
// .get_or_insert_with(|| self.resolver.resolve(&self.events))
|
||||
self.resolver.resolve(&self.events)
|
||||
pub fn get_resolver(&self) -> &dyn Resolver<Type = T, State = S> {
|
||||
match &self.get_root().content {
|
||||
CoreContent::Create(create) => create.resolver.as_ref(),
|
||||
_ => unreachable!("the root event is always a create event"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_event(&mut self, event_content: CoreContent<T>) {
|
||||
pub fn get_state(&mut self) -> S {
|
||||
let resolver = self.get_resolver();
|
||||
let sorted = sort(|a, b| resolver.tiebreak(a, b), &self.events);
|
||||
resolver.resolve(&sorted)
|
||||
}
|
||||
|
||||
pub fn create_event(&mut self, event_content: CoreContent<T, S>) {
|
||||
let event = Event {
|
||||
id: EventId::random(),
|
||||
content: event_content,
|
||||
|
@ -43,7 +47,7 @@ impl<T: Debug, S> Room<T, S> {
|
|||
self.append_event(event);
|
||||
}
|
||||
|
||||
pub fn append_event(&mut self, event: Event<T>) {
|
||||
pub fn append_event(&mut self, event: Event<T, S>) {
|
||||
// let parents: Vec<_> = self
|
||||
// .events
|
||||
// .iter()
|
||||
|
|
Loading…
Reference in a new issue