move resolver code out of event for now

This commit is contained in:
tezlm 2024-02-07 14:13:10 -08:00
parent e208f62686
commit 0c96c9e7bf
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
9 changed files with 168 additions and 158 deletions

56
Cargo.lock generated
View file

@ -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"

View file

@ -8,3 +8,4 @@ edition = "2021"
[dependencies]
petgraph = "0.6.4"
rand = "0.8.5"
thiserror = "1.0.56"

View file

@ -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
View file

@ -0,0 +1,4 @@
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {}

View file

@ -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,
}

View file

@ -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")));
}

View file

@ -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
View 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
}
}

View file

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