make more generic
This commit is contained in:
parent
51a4875313
commit
5979337cb8
3 changed files with 90 additions and 60 deletions
17
src/main.rs
17
src/main.rs
|
@ -1,7 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::room::Room;
|
||||
use petgraph::algo::toposort;
|
||||
use crate::{resolver::Resolver, room::Room};
|
||||
|
||||
mod atoms;
|
||||
mod event;
|
||||
|
@ -15,11 +13,11 @@ enum EventContent {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let mut room = Room::new(Box::new(|graph| {
|
||||
let sorted = toposort(graph, None).expect("graph is cyclic!");
|
||||
let resolver = Resolver::new(Box::new(|events| {
|
||||
dbg!(events);
|
||||
let mut kv = HashMap::new();
|
||||
for id in sorted {
|
||||
match &graph[id].content {
|
||||
for event in events {
|
||||
match &event.content {
|
||||
EventContent::Create => {},
|
||||
EventContent::Set(k, v) => {
|
||||
kv.insert(k.clone(), v.clone());
|
||||
|
@ -27,7 +25,10 @@ fn main() {
|
|||
}
|
||||
}
|
||||
kv
|
||||
}), EventContent::Create);
|
||||
}), Box::new(|_a, _b| {
|
||||
todo!()
|
||||
}));
|
||||
let mut room = Room::new(resolver, EventContent::Create);
|
||||
room.create_event(EventContent::Set("foo".into(), "apple".into()));
|
||||
room.create_event(EventContent::Set("bar".into(), "banana".into()));
|
||||
room.create_event(EventContent::Set("baz".into(), "carrot".into()));
|
||||
|
|
|
@ -1,14 +1,61 @@
|
|||
use crate::event::Event;
|
||||
use petgraph::Directed;
|
||||
use std::fmt::Debug;
|
||||
use crate::{atoms::EventId, event::Event};
|
||||
use std::{cmp::Ordering, collections::HashSet, fmt::Debug, ops::Deref};
|
||||
|
||||
pub type Events<T> = petgraph::Graph<Event<T>, (), Directed>;
|
||||
pub type ResolveFn<T, S> = Box<dyn Fn(&Events<T>) -> S>;
|
||||
pub struct Resolver<T, S>(pub ResolveFn<T, S>);
|
||||
pub type TiebreakFn<T> = Box<dyn Fn(&Event<T>, &Event<T>) -> Ordering>;
|
||||
pub type ResolveFn<T, S> = Box<dyn Fn(&[&Event<T>]) -> S>;
|
||||
|
||||
impl<T, S> Resolver<T, S> {
|
||||
pub fn new(resolve: ResolveFn<T, S>) -> Self {
|
||||
Resolver(resolve)
|
||||
pub struct Resolver<T, S> {
|
||||
/// Given two events, decide which one comes first
|
||||
pub tiebreak_fn: TiebreakFn<T>,
|
||||
|
||||
/// Given a set of ordered events, resolve the final state
|
||||
pub resolve_fn: ResolveFn<T, S>,
|
||||
}
|
||||
|
||||
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()
|
||||
.map(|event| {
|
||||
event
|
||||
.references
|
||||
.iter()
|
||||
.map(|r| (r.clone(), event.id.clone()))
|
||||
})
|
||||
.flatten()
|
||||
.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, _)| parent != &ev.id);
|
||||
sorted.push(ev);
|
||||
let (mut children, new_unsorted) = unsorted.into_iter().partition(|candidate| {
|
||||
references
|
||||
.iter()
|
||||
.position(|(_, child)| child == &candidate.id)
|
||||
.is_none()
|
||||
});
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
68
src/room.rs
68
src/room.rs
|
@ -1,74 +1,56 @@
|
|||
use petgraph::{adj::NodeIndex, Graph};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{atoms::EventId, event::Event, resolver::{Events, ResolveFn, Resolver}};
|
||||
use crate::{atoms::EventId, event::Event, resolver::Resolver};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Room<E, S> {
|
||||
pub(super) resolver: Resolver<E, S>,
|
||||
pub(super) events: Events<E>,
|
||||
pub(super) state_cache: Option<S>,
|
||||
pub(super) root_idx: NodeIndex,
|
||||
pub(super) heads: Vec<NodeIndex>,
|
||||
pub(super) events: Vec<Event<E>>,
|
||||
pub(super) heads: Vec<EventId>,
|
||||
}
|
||||
|
||||
impl<T, S> Room<T, S> {
|
||||
pub fn new(resolve: ResolveFn<T, S>, base_content: T) -> Self {
|
||||
impl<T: Debug, S> Room<T, S> {
|
||||
pub fn new(resolver: Resolver<T, S>, base_content: T) -> Self {
|
||||
let event_id = EventId::random();
|
||||
let base_event = Event {
|
||||
id: EventId::random(),
|
||||
id: event_id.clone(),
|
||||
content: base_content,
|
||||
references: vec![],
|
||||
};
|
||||
let mut events: Events<T> = Graph::new();
|
||||
let root_idx = events.add_node(base_event);
|
||||
Self {
|
||||
resolver: Resolver::new(resolve),
|
||||
events,
|
||||
state_cache: None,
|
||||
root_idx: root_idx.index() as u32,
|
||||
heads: vec![root_idx.index() as u32],
|
||||
resolver,
|
||||
events: vec![base_event],
|
||||
heads: vec![event_id],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_root(&self) -> &Event<T> {
|
||||
self.events
|
||||
.node_weight(self.root_idx.into())
|
||||
.expect("missing root event!")
|
||||
self.events.get(0).as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_state(&mut self) -> &S {
|
||||
self.state_cache
|
||||
.get_or_insert_with(|| self.resolver.0(&self.events))
|
||||
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 create_event(&mut self, event_content: T) {
|
||||
let event = Event {
|
||||
id: EventId::random(),
|
||||
content: event_content,
|
||||
references: self
|
||||
.heads
|
||||
.iter()
|
||||
.map(|id| {
|
||||
self.events
|
||||
.node_weight((*id).into())
|
||||
.expect("missing event!")
|
||||
.id
|
||||
.clone()
|
||||
})
|
||||
.collect(),
|
||||
references: std::mem::take(&mut self.heads),
|
||||
};
|
||||
self.append_event(event);
|
||||
}
|
||||
|
||||
pub fn append_event(&mut self, event: Event<T>) {
|
||||
let parents: Vec<_> = self
|
||||
.events
|
||||
.node_indices()
|
||||
.filter(|i| event.references.contains(&self.events[*i].id))
|
||||
.collect();
|
||||
let event_ref = self.events.add_node(event);
|
||||
for parent in parents {
|
||||
self.events.add_edge(event_ref, parent, ());
|
||||
}
|
||||
self.heads = vec![event_ref.index() as u32];
|
||||
// let parents: Vec<_> = self
|
||||
// .events
|
||||
// .iter()
|
||||
// .filter(|parent| event.references.contains(&parent.id))
|
||||
// .collect();
|
||||
let event_id = event.id.clone();
|
||||
self.events.push(event);
|
||||
self.heads = vec![event_id];
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue