move resolver code out of event for now
This commit is contained in:
parent
e208f62686
commit
0c96c9e7bf
9 changed files with 168 additions and 158 deletions
56
Cargo.lock
generated
56
Cargo.lock
generated
|
@ -14,6 +14,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"petgraph",
|
||||
"rand",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -77,6 +78,24 @@ version = "0.2.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
|
@ -107,6 +126,43 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
|
|
@ -8,3 +8,4 @@ edition = "2021"
|
|||
[dependencies]
|
||||
petgraph = "0.6.4"
|
||||
rand = "0.8.5"
|
||||
thiserror = "1.0.56"
|
||||
|
|
|
@ -13,6 +13,7 @@ pub struct ActorId(String);
|
|||
|
||||
impl RoomId {
|
||||
// maybe use the initial create event hash/id as the room id?
|
||||
// ie. !foobar refers to the room while $foobar refers to the event
|
||||
pub fn random() -> Self {
|
||||
let rng = rand::thread_rng()
|
||||
.sample_iter(Alphanumeric)
|
||||
|
@ -53,7 +54,7 @@ macro_rules! impl_display {
|
|||
write!(f, $fmt, self.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Debug for $Thing {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(self, f)
|
||||
|
|
4
src/error.rs
Normal file
4
src/error.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {}
|
26
src/event.rs
26
src/event.rs
|
@ -1,12 +1,11 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use crate::{atoms::EventId, resolver::Resolver};
|
||||
use crate::atoms::EventId;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// An event, is the basic building block/envolope containing all data.
|
||||
pub struct Event<T, S> {
|
||||
pub struct Event<T> {
|
||||
pub id: EventId,
|
||||
pub content: CoreContent<T, S>,
|
||||
pub content: CoreContent<T>,
|
||||
pub references: Vec<EventId>,
|
||||
// pub author: ActorId,
|
||||
// pub signature: ActorSignature,
|
||||
|
@ -14,13 +13,12 @@ pub struct Event<T, S> {
|
|||
|
||||
#[derive(Debug)]
|
||||
/// This defines content of an event as either a builtin event or application-specific event.
|
||||
pub enum CoreContent<T, S> {
|
||||
Create(CreateContent<T, S>),
|
||||
pub enum CoreContent<T> {
|
||||
Create(CreateContent),
|
||||
Custom(T),
|
||||
|
||||
/*
|
||||
custom events from other places, for inspiration
|
||||
|
||||
|
||||
ufh (my failed project):
|
||||
- x.tag{.local} to add tags to events
|
||||
- x.annotate{.local} to add custom data to events
|
||||
|
@ -43,14 +41,6 @@ pub enum CoreContent<T, S> {
|
|||
|
||||
#[derive(Debug)]
|
||||
/// Every room has exactly one immutable create event as the single root of the event dag.
|
||||
pub struct CreateContent<T, S> {
|
||||
pub resolver: Box<dyn Resolver<Type = T, State = S>>,
|
||||
}
|
||||
|
||||
impl<T, S> CreateContent<T, S> {
|
||||
pub fn new(resolver: Box<dyn Resolver<Type = T, State = S>>) -> Self {
|
||||
Self {
|
||||
resolver,
|
||||
}
|
||||
}
|
||||
pub struct CreateContent {
|
||||
pub resolver: String,
|
||||
}
|
||||
|
|
130
src/main.rs
130
src/main.rs
|
@ -1,115 +1,23 @@
|
|||
#![allow(dead_code)] // TODO: remove this later!
|
||||
|
||||
use event::Event;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{BTreeMap, HashMap},
|
||||
path::{Path, PathBuf},
|
||||
use crate::{
|
||||
event::CoreContent,
|
||||
resolvers::{KVEventContent, PremadeResolver},
|
||||
room::Room,
|
||||
};
|
||||
|
||||
use crate::{event::CoreContent, resolver::Resolver, room::Room};
|
||||
|
||||
mod atoms;
|
||||
mod error;
|
||||
mod event;
|
||||
mod resolver;
|
||||
mod resolvers;
|
||||
mod room;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A basic key value store
|
||||
enum KVEventContent {
|
||||
Set(String, String),
|
||||
}
|
||||
|
||||
#[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(KVEventContent::Set(k, v)) => {
|
||||
kv.insert(k.clone(), v.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
kv
|
||||
}
|
||||
|
||||
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));
|
||||
let resolver = match PremadeResolver::get("kv") {
|
||||
PremadeResolver::KVResolver(resolver) => resolver,
|
||||
};
|
||||
let mut room = Room::new("kv", Box::new(resolver));
|
||||
room.create_event(CoreContent::Custom(KVEventContent::Set(
|
||||
"foo".into(),
|
||||
"apple".into(),
|
||||
|
@ -124,22 +32,4 @@ fn main() {
|
|||
)));
|
||||
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,26 +1,29 @@
|
|||
use crate::{atoms::EventId, event::Event};
|
||||
use std::{cmp::Ordering, collections::HashSet, fmt::Debug};
|
||||
|
||||
///! small shards of code designed to resolve state
|
||||
|
||||
pub trait Resolver {
|
||||
type Type;
|
||||
type State;
|
||||
|
||||
/// Given a set of ordered events, resolve the final state
|
||||
fn resolve(&self, events: &[&Event<Self::Type, Self::State>]) -> Self::State;
|
||||
fn resolve(&self, events: &[&Event<Self::Type>]) -> Self::State;
|
||||
|
||||
/// Given two events, decide which one comes first
|
||||
/// if Ordering::Equal is returned, the event id is used
|
||||
fn tiebreak(
|
||||
&self,
|
||||
a: &Event<Self::Type, Self::State>,
|
||||
b: &Event<Self::Type, Self::State>,
|
||||
a: &Event<Self::Type>,
|
||||
b: &Event<Self::Type>,
|
||||
) -> Ordering;
|
||||
}
|
||||
|
||||
/// 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>> {
|
||||
pub fn sort<T>(
|
||||
tiebreak: impl Fn(&Event<T>, &Event<T>) -> Ordering,
|
||||
events: &[Event<T>],
|
||||
) -> Vec<&Event<T>> {
|
||||
let mut references: HashSet<(EventId, EventId)> = events
|
||||
.iter()
|
||||
.flat_map(|event| {
|
||||
|
|
60
src/resolvers.rs
Normal file
60
src/resolvers.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
use crate::{
|
||||
event::{CoreContent, Event}, resolver::Resolver
|
||||
};
|
||||
use std::{cmp::Ordering, collections::BTreeMap};
|
||||
|
||||
///! contains premade resolvers, for testing
|
||||
|
||||
// type KVRoom = Room<KVEventContent, KVState>;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum PremadeResolver {
|
||||
KVResolver(KVResolver),
|
||||
}
|
||||
|
||||
impl PremadeResolver {
|
||||
pub fn get(name: &str) -> PremadeResolver {
|
||||
match name {
|
||||
"kv" => PremadeResolver::KVResolver(KVResolver),
|
||||
_ => unimplemented!("resolver does not exist"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A basic key-value store
|
||||
pub struct KVResolver;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum KVEventContent {
|
||||
Set(String, String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KVState {
|
||||
entries: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
impl Resolver for KVResolver {
|
||||
type Type = KVEventContent;
|
||||
type State = KVState;
|
||||
|
||||
fn resolve(&self, events: &[&Event<Self::Type>]) -> Self::State {
|
||||
dbg!(events);
|
||||
let mut kv = BTreeMap::new();
|
||||
for event in events {
|
||||
match &event.content {
|
||||
CoreContent::Create(_) => {}
|
||||
CoreContent::Custom(KVEventContent::Set(k, v)) => {
|
||||
kv.insert(k.clone(), v.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
KVState { entries: kv }
|
||||
}
|
||||
|
||||
fn tiebreak(&self, _a: &Event<Self::Type>, _b: &Event<Self::Type>) -> Ordering {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
29
src/room.rs
29
src/room.rs
|
@ -1,35 +1,40 @@
|
|||
use crate::{
|
||||
atoms::EventId,
|
||||
event::{CoreContent, CreateContent, Event},
|
||||
resolver::{sort, Resolver},
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
use crate::{atoms::EventId, event::{CoreContent, CreateContent, Event}, resolver::{sort, Resolver}};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Room<E, S> {
|
||||
pub(super) events: Vec<Event<E, S>>,
|
||||
pub(super) heads: Vec<EventId>,
|
||||
pub events: Vec<Event<E>>,
|
||||
pub heads: Vec<EventId>,
|
||||
pub resolver: Box<dyn Resolver<Type = E, State = S>>,
|
||||
}
|
||||
|
||||
impl<T: Debug, S> Room<T, S> {
|
||||
pub fn new(resolver: Box<dyn Resolver<Type = T, State = S>>) -> Self {
|
||||
pub fn new(resolver_name: impl Into<String>, 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(resolver)),
|
||||
content: CoreContent::Create(CreateContent {
|
||||
resolver: resolver_name.into(),
|
||||
}),
|
||||
references: vec![],
|
||||
};
|
||||
Self {
|
||||
events: vec![base_event],
|
||||
heads: vec![event_id],
|
||||
resolver,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_root(&self) -> &Event<T, S> {
|
||||
pub fn get_root(&self) -> &Event<T> {
|
||||
self.events.first().as_ref().unwrap()
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
self.resolver.as_ref()
|
||||
}
|
||||
|
||||
pub fn get_state(&mut self) -> S {
|
||||
|
@ -38,7 +43,7 @@ impl<T: Debug, S> Room<T, S> {
|
|||
resolver.resolve(&sorted)
|
||||
}
|
||||
|
||||
pub fn create_event(&mut self, event_content: CoreContent<T, S>) {
|
||||
pub fn create_event(&mut self, event_content: CoreContent<T>) {
|
||||
let event = Event {
|
||||
id: EventId::random(),
|
||||
content: event_content,
|
||||
|
@ -47,7 +52,7 @@ impl<T: Debug, S> Room<T, S> {
|
|||
self.append_event(event);
|
||||
}
|
||||
|
||||
pub fn append_event(&mut self, event: Event<T, S>) {
|
||||
pub fn append_event(&mut self, event: Event<T>) {
|
||||
// let parents: Vec<_> = self
|
||||
// .events
|
||||
// .iter()
|
||||
|
|
Loading…
Reference in a new issue