make temporary gui hack use sqlite store

This commit is contained in:
tezlm 2024-02-12 23:17:35 -08:00
parent c3c41a6092
commit 4d5cd581da
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
8 changed files with 180 additions and 83 deletions

15
Cargo.lock generated
View file

@ -445,18 +445,18 @@ dependencies = [
[[package]] [[package]]
name = "clipboard_wayland" name = "clipboard_wayland"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8134163bd07c47ae3cc29babc42c255fdb315facc790950ae2d0e561ea6f2ec0" checksum = "003f886bc4e2987729d10c1db3424e7f80809f3fc22dbc16c685738887cb37b8"
dependencies = [ dependencies = [
"smithay-clipboard", "smithay-clipboard",
] ]
[[package]] [[package]]
name = "clipboard_x11" name = "clipboard_x11"
version = "0.4.1" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cf45b436634fee64c6d3981639b46a87eeea3c64e422643273fcefd1baef56c" checksum = "4274ea815e013e0f9f04a2633423e14194e408a0576c943ce3d14ca56c50031c"
dependencies = [ dependencies = [
"thiserror", "thiserror",
"x11rb 0.13.0", "x11rb 0.13.0",
@ -1121,8 +1121,11 @@ dependencies = [
name = "gui" name = "gui"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"dag-resolve", "dag-resolve",
"iced", "iced",
"rusqlite",
"serde_json",
] ]
[[package]] [[package]]
@ -3175,9 +3178,9 @@ dependencies = [
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.68" version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",

View file

@ -214,7 +214,7 @@ fn startup(path: impl AsRef<Path>, create: Option<&str>) -> Result<Opened> {
} }
} }
fn sync_state<R: Resolver + Debug>(mut from: State<R>, mut to: State<R>) -> Result<()> { fn sync_state<R: Resolver + Debug>(from: &mut State<R>, to: &mut State<R>) -> Result<()> {
if from.room.get_root() != to.room.get_root() { if from.room.get_root() != to.room.get_root() {
panic!("cannot sync events from two different rooms"); panic!("cannot sync events from two different rooms");
} }
@ -224,18 +224,16 @@ fn sync_state<R: Resolver + Debug>(mut from: State<R>, mut to: State<R>) -> Resu
for event in &to.room.events { for event in &to.room.events {
from.append_event(event.clone())?; from.append_event(event.clone())?;
} }
dbg!( from.room.resolve_state(&mut *from.store);
from.room.resolve_state(*from.store), to.room.resolve_state(&mut *to.store);
to.room.resolve_state(*to.store) dbg!(&from.store, &to.store);
);
dbg!(from.room);
Ok(()) Ok(())
} }
fn send_event<R: Resolver + Debug>(mut state: State<R>, data: &str) -> Result<()> { fn send_event<R: Resolver + Debug>(state: &mut State<R>, data: &str) -> Result<()> {
state.create_event(dbg!(serde_json::from_str(dbg!(data))?))?; state.create_event(dbg!(serde_json::from_str(dbg!(data))?))?;
dbg!(&state.room); dbg!(&state.room);
dbg!(&state.room.resolve_state(*state.store)); dbg!(&state.room.resolve_state(&mut *state.store));
Ok(()) Ok(())
} }
@ -272,19 +270,19 @@ fn main() -> Result<()> {
}; };
match (cli, opened) { match (cli, opened) {
(Cli::Init { .. }, _) => {}, (Cli::Init { .. }, _) => {},
(Cli::Send { repo: _, data }, Opened::Kv(state)) => send_event(state, &data)?, (Cli::Send { repo: _, data }, Opened::Kv(mut state)) => send_event(&mut state, &data)?,
(Cli::Send { repo: _, data }, Opened::Forum(state)) => send_event(state, &data)?, (Cli::Send { repo: _, data }, Opened::Forum(mut state)) => send_event(&mut state, &data)?,
(Cli::Info { repo: _ }, Opened::Kv(state)) => print_info(state)?, (Cli::Info { repo: _ }, Opened::Kv(state)) => print_info(state)?,
(Cli::Info { repo: _ }, Opened::Forum(state)) => print_info(state)?, (Cli::Info { repo: _ }, Opened::Forum(state)) => print_info(state)?,
(Cli::Sync { repo: _, other }, Opened::Kv(our_state)) => { (Cli::Sync { repo: _, other }, Opened::Kv(mut our_state)) => {
match startup(other, None)? { match startup(other, None)? {
Opened::Kv(their_state) => sync_state(our_state, their_state)?, Opened::Kv(mut their_state) => sync_state(&mut our_state, &mut their_state)?,
_ => panic!("cannot sync different room types"), _ => panic!("cannot sync different room types"),
} }
}, },
(Cli::Sync { repo: _, other }, Opened::Forum(our_state)) => { (Cli::Sync { repo: _, other }, Opened::Forum(mut our_state)) => {
match startup(other, None)? { match startup(other, None)? {
Opened::Forum(their_state) => sync_state(our_state, their_state)?, Opened::Forum(mut their_state) => sync_state(&mut our_state, &mut their_state)?,
_ => panic!("cannot sync different room types"), _ => panic!("cannot sync different room types"),
} }
}, },

View file

@ -6,5 +6,8 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = "1.0.79"
dag-resolve = { version = "0.1.0", path = "../lib" } dag-resolve = { version = "0.1.0", path = "../lib" }
iced = { version = "0.10.0", features = ["debug"] } iced = { version = "0.10.0", features = ["debug"] }
rusqlite = { version = "0.30.0", features = ["bundled"] }
serde_json = "1.0.113"

View file

@ -1,13 +1,13 @@
use std::sync::Mutex; use std::{path::Path, rc::Rc, sync::Mutex};
use anyhow::Result;
use dag_resolve::{ use dag_resolve::{
actor::{ActorId, ActorSecret}, actor::{ActorId, ActorSecret},
event::{CoreContent, HashType, SignatureType}, event::{CoreContent, Event, HashType, SignatureType},
proto::table::{State, Table}, proto::table::{State as _, Table as _},
resolver::Resolver, resolver::Resolver,
resolvers::kv::{KVEventContent, KVResolver}, resolvers::kv::{KVEventContent, KVResolver},
room::Room, room::Room,
store::memory::MemoryStore,
}; };
use iced::{ use iced::{
keyboard::KeyCode, keyboard::KeyCode,
@ -16,13 +16,11 @@ use iced::{
}; };
struct Flags { struct Flags {
room: Room<KVResolver>, state: State<KVResolver>,
secret: ActorSecret,
} }
struct Counter { struct Main {
room: Mutex<Room<KVResolver>>, state: Mutex<State<KVResolver>>,
secret: ActorSecret,
key: String, key: String,
value: String, value: String,
key_id: text_input::Id, key_id: text_input::Id,
@ -39,17 +37,16 @@ enum Message {
FocusPrev, FocusPrev,
} }
impl Application for Counter { impl Application for Main {
type Executor = iced::executor::Default; type Executor = iced::executor::Default;
type Message = Message; type Message = Message;
type Theme = iced::Theme; type Theme = iced::Theme;
type Flags = Flags; type Flags = Flags;
fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) { fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) {
let room = Mutex::new(flags.room); let state = Mutex::new(flags.state);
let state = Self { let state = Self {
room, state,
secret: flags.secret,
key: String::new(), key: String::new(),
value: String::new(), value: String::new(),
key_id: text_input::Id::unique(), key_id: text_input::Id::unique(),
@ -74,14 +71,11 @@ impl Application for Counter {
let key = std::mem::take(&mut self.key); let key = std::mem::take(&mut self.key);
let value = std::mem::take(&mut self.value); let value = std::mem::take(&mut self.value);
self.room self.state
.lock() .lock()
.unwrap() .unwrap()
.create_event( .create_event(CoreContent::Custom(KVEventContent::Set(key, value)))
CoreContent::Custom(KVEventContent::Set(key, value)), .unwrap()
&self.secret,
)
.unwrap();
} }
Message::FocusNext => { Message::FocusNext => {
let next = match &self.focused { let next = match &self.focused {
@ -106,13 +100,14 @@ impl Application for Counter {
} }
fn view(&self) -> Element<'_, Self::Message> { fn view(&self) -> Element<'_, Self::Message> {
let mut room = self.room.lock().unwrap(); let mut state = self.state.lock().unwrap();
let store = MemoryStore::new()
.init(room.get_resolver().get_state_config()) fn resolve<R: Resolver>(state: &mut State<R>) {
.unwrap(); state.room.resolve_state(&mut *state.store);
let state = room.resolve_state(*store); }
dbg!(&state);
let rows = state.table("kv").unwrap().all().unwrap(); resolve(&mut state);
let rows = state.store.table("kv").unwrap().all().unwrap();
let mut column = Column::new(); let mut column = Column::new();
column = column.push(text(format!("{} keys", rows.len())).size(24)); column = column.push(text(format!("{} keys", rows.len())).size(24));
for row in rows { for row in rows {
@ -159,22 +154,105 @@ impl Application for Counter {
} }
} }
fn main() { #[derive(Debug)]
let (_actor, secret) = ActorId::new(SignatureType::Ed25519); struct State<R: Resolver> {
let room = Room::builder() db: Rc<rusqlite::Connection>,
.with_hasher(HashType::Sha256) room: Room<R>,
.with_signer(SignatureType::Ed25519) secret: ActorSecret,
.with_resolver(KVResolver) store: Box<dag_resolve::store::sqlite::Database>,
.create(&secret) }
.unwrap();
Counter::run(Settings { impl<R: Resolver> State<R> {
fn create_event(&mut self, content: CoreContent<R::EventType>) -> Result<()> {
match self.room.create_event(content, &self.secret) {
Ok(event) => {
self.db.execute(
"INSERT INTO _events (id, json) VALUES (?, ?)",
(event.id().to_string(), serde_json::to_string(&event)?),
)?;
Ok(())
}
Err(dag_resolve::Error::AlreadyExists) => Ok(()),
Err(err) => Err(err.into()),
}
}
fn append_event(&mut self, event: Event<R::EventType>) -> Result<()> {
match self.room.append_event(event) {
Ok(event) => {
self.db.execute(
"INSERT INTO _events (id, json) VALUES (?, ?)",
(event.id().to_string(), serde_json::to_string(&event)?),
)?;
Ok(())
}
Err(dag_resolve::Error::AlreadyExists) => Ok(()),
Err(err) => Err(err.into()),
}
}
}
fn open(path: impl AsRef<Path>) -> Result<State<KVResolver>> {
// restore repo
let db = rusqlite::Connection::open(path)?;
let actor: ActorId = db.query_row(
"SELECT value FROM _config WHERE key='actor_id'",
[],
|row| row.get(0).map(|s: String| s.parse()),
)??;
let secret: ActorSecret = db.query_row(
"SELECT value FROM _config WHERE key='actor_secret'",
[],
|row| row.get(0).map(|s: String| serde_json::from_str(&s)),
)??;
dbg!(&actor, &secret);
let mut q = db.prepare("SELECT json FROM _events")?;
let mut rows = q.query([])?;
let event_json: String = rows
.next()?
.expect("there's always one root event")
.get(0)?;
let event: Event<()> = serde_json::from_str(&event_json)?;
match event.content() {
CoreContent::Create(c) => match c.resolver.as_str() {
"kv" => {
let mut room = Room::from_root(KVResolver, serde_json::from_str(&event_json)?)?;
while let Some(row) = rows.next()? {
let s: String = row.get(0)?;
let ev = serde_json::from_str(&s)?;
room.append_event(ev)?;
}
drop(event);
drop(rows);
drop(q);
dbg!(&room);
let db = Rc::new(db);
let store = dag_resolve::store::sqlite::Database::from_conn(db.clone())
.init(room.get_resolver().get_state_config())?;
Ok(State {
db,
room,
secret,
store,
})
}
_ => unimplemented!("unknown or unimplemented resolver"),
},
_ => panic!("tried to start with non-create event"),
}
}
fn main() -> Result<()> {
let state = open("room.db")?;
Main::run(Settings {
default_font: Font::with_name("Hind Siliguri"), default_font: Font::with_name("Hind Siliguri"),
flags: Flags { room, secret }, flags: Flags { state },
id: None, id: None,
window: iced::window::Settings::default(), window: iced::window::Settings::default(),
default_text_size: 16.0, default_text_size: 16.0,
antialiasing: false, antialiasing: false,
exit_on_close_request: true, exit_on_close_request: true,
}) })?;
.unwrap(); Ok(())
} }

View file

@ -65,15 +65,15 @@ impl<R: Resolver> Room<R> {
&self.resolver &self.resolver
} }
pub fn resolve_state<S>(&mut self, initial_state: S) -> S pub fn resolve_state<S>(&self, state: &mut S)
where where
S: proto::table::State, S: proto::table::State,
{ {
let resolver = self.get_resolver(); let resolver = self.get_resolver();
let sorted = sort(|a, b| resolver.tiebreak(a, b), &self.events); let sorted = sort(|a, b| resolver.tiebreak(a, b), &self.events);
let state = initial_state; state.reset().unwrap();
for event in sorted { for event in sorted {
let effects = resolver.resolve(&state, event); let effects = resolver.resolve(state, event);
for effect in effects { for effect in effects {
match effect { match effect {
ResolverEffect::Insert { table, row } => { ResolverEffect::Insert { table, row } => {
@ -82,7 +82,6 @@ impl<R: Resolver> Room<R> {
} }
} }
} }
state
} }
pub fn create_event( pub fn create_event(

View file

@ -109,7 +109,11 @@ pub trait State: Debug {
type Err: Debug; type Err: Debug;
type Table: Table; type Table: Table;
/// Get a table
fn table(&self, name: &str) -> Result<Self::Table, Self::Err>; fn table(&self, name: &str) -> Result<Self::Table, Self::Err>;
/// TEMP: clear everything in the table. In the future, there will be methods for incremental reduction.
fn reset(&mut self) -> Result<(), Self::Err>;
} }
pub trait Table { pub trait Table {

View file

@ -40,6 +40,13 @@ impl State for MemoryStore {
fn table(&self, name: &str) -> Result<Self::Table, Self::Err> { fn table(&self, name: &str) -> Result<Self::Table, Self::Err> {
self.tables.get(name).cloned().ok_or(()) self.tables.get(name).cloned().ok_or(())
} }
fn reset(&mut self) -> Result<(), Self::Err> {
for table in self.tables.values_mut() {
table.map.write().unwrap().clear();
}
Ok(())
}
} }
impl Table for MemoryTable { impl Table for MemoryTable {

View file

@ -2,7 +2,7 @@
use std::{collections::HashMap, path::Path, rc::Rc}; use std::{collections::HashMap, path::Path, rc::Rc};
use crate::proto::table; use crate::proto::table::{self, State};
use rusqlite::params_from_iter; use rusqlite::params_from_iter;
use thiserror::Error; use thiserror::Error;
@ -39,8 +39,30 @@ impl Database {
} }
pub fn init(mut self, config: table::StateConfig) -> Result<Box<Self>, Error> { pub fn init(mut self, config: table::StateConfig) -> Result<Box<Self>, Error> {
self.config = Some(config);
self.reset()?;
Ok(Box::new(self))
}
}
impl table::State for Database {
type Table = Table;
type Err = Error;
fn table(&self, name: &str) -> Result<Table, Self::Err> {
let Some(table) = self.config.as_ref().unwrap().tables.get(name) else {
panic!("table does not exist");
};
Ok(Table {
connection: self.connection.clone(),
name: name.to_owned(),
config: table.to_owned(),
})
}
fn reset(&mut self) -> Result<(), Self::Err> {
let mut sql = String::new(); let mut sql = String::new();
for (table_name, table_column) in &config.tables { for (table_name, table_column) in &self.config.as_ref().unwrap().tables {
// FIXME: don't drop and resolve from scratch every time // FIXME: don't drop and resolve from scratch every time
sql.push_str("DROP TABLE IF EXISTS '"); sql.push_str("DROP TABLE IF EXISTS '");
sql.push_str(&table_name.replace('\'', "''")); sql.push_str(&table_name.replace('\'', "''"));
@ -64,24 +86,7 @@ impl Database {
sql.push_str(");\n"); sql.push_str(");\n");
} }
self.connection.execute_batch(&sql).unwrap(); self.connection.execute_batch(&sql).unwrap();
self.config = Some(config); Ok(())
Ok(Box::new(self))
}
}
impl table::State for Database {
type Table = Table;
type Err = Error;
fn table(&self, name: &str) -> Result<Table, Self::Err> {
let Some(table) = self.config.as_ref().unwrap().tables.get(name) else {
panic!("table does not exist");
};
Ok(Table {
connection: self.connection.clone(),
name: name.to_owned(),
config: table.to_owned(),
})
} }
} }
@ -89,7 +94,7 @@ impl table::Table for Table {
type Err = Error; type Err = Error;
fn all(&self) -> Result<Vec<table::TableRow>, Self::Err> { fn all(&self) -> Result<Vec<table::TableRow>, Self::Err> {
let mut sql = String::from("SELECT * FROM "); let mut sql = String::from("SELECT * FROM '");
sql.push_str(&self.name.replace('\'', "''")); sql.push_str(&self.name.replace('\'', "''"));
sql.push('\''); sql.push('\'');
let mut sql_query = self.connection.prepare(&sql).unwrap(); let mut sql_query = self.connection.prepare(&sql).unwrap();