make more generic

This commit is contained in:
tezlm 2024-02-02 14:27:48 -08:00
parent 51a4875313
commit 5979337cb8
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
3 changed files with 90 additions and 60 deletions

View file

@ -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()));

View file

@ -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())
}
}

View file

@ -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];
}
}