make temporary gui hack use sqlite store
This commit is contained in:
parent
c3c41a6092
commit
4d5cd581da
8 changed files with 180 additions and 83 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -445,18 +445,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clipboard_wayland"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8134163bd07c47ae3cc29babc42c255fdb315facc790950ae2d0e561ea6f2ec0"
|
||||
checksum = "003f886bc4e2987729d10c1db3424e7f80809f3fc22dbc16c685738887cb37b8"
|
||||
dependencies = [
|
||||
"smithay-clipboard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard_x11"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cf45b436634fee64c6d3981639b46a87eeea3c64e422643273fcefd1baef56c"
|
||||
checksum = "4274ea815e013e0f9f04a2633423e14194e408a0576c943ce3d14ca56c50031c"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"x11rb 0.13.0",
|
||||
|
@ -1121,8 +1121,11 @@ dependencies = [
|
|||
name = "gui"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dag-resolve",
|
||||
"iced",
|
||||
"rusqlite",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3175,9 +3178,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.68"
|
||||
version = "0.3.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
|
||||
checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
|
|
@ -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() {
|
||||
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 {
|
||||
from.append_event(event.clone())?;
|
||||
}
|
||||
dbg!(
|
||||
from.room.resolve_state(*from.store),
|
||||
to.room.resolve_state(*to.store)
|
||||
);
|
||||
dbg!(from.room);
|
||||
from.room.resolve_state(&mut *from.store);
|
||||
to.room.resolve_state(&mut *to.store);
|
||||
dbg!(&from.store, &to.store);
|
||||
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))?))?;
|
||||
dbg!(&state.room);
|
||||
dbg!(&state.room.resolve_state(*state.store));
|
||||
dbg!(&state.room.resolve_state(&mut *state.store));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -272,19 +270,19 @@ fn main() -> Result<()> {
|
|||
};
|
||||
match (cli, opened) {
|
||||
(Cli::Init { .. }, _) => {},
|
||||
(Cli::Send { repo: _, data }, Opened::Kv(state)) => send_event(state, &data)?,
|
||||
(Cli::Send { repo: _, data }, Opened::Forum(state)) => send_event(state, &data)?,
|
||||
(Cli::Send { repo: _, data }, Opened::Kv(mut state)) => send_event(&mut 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::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)? {
|
||||
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"),
|
||||
}
|
||||
},
|
||||
(Cli::Sync { repo: _, other }, Opened::Forum(our_state)) => {
|
||||
(Cli::Sync { repo: _, other }, Opened::Forum(mut our_state)) => {
|
||||
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"),
|
||||
}
|
||||
},
|
||||
|
|
|
@ -6,5 +6,8 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.79"
|
||||
dag-resolve = { version = "0.1.0", path = "../lib" }
|
||||
iced = { version = "0.10.0", features = ["debug"] }
|
||||
rusqlite = { version = "0.30.0", features = ["bundled"] }
|
||||
serde_json = "1.0.113"
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use std::sync::Mutex;
|
||||
use std::{path::Path, rc::Rc, sync::Mutex};
|
||||
|
||||
use anyhow::Result;
|
||||
use dag_resolve::{
|
||||
actor::{ActorId, ActorSecret},
|
||||
event::{CoreContent, HashType, SignatureType},
|
||||
proto::table::{State, Table},
|
||||
event::{CoreContent, Event, HashType, SignatureType},
|
||||
proto::table::{State as _, Table as _},
|
||||
resolver::Resolver,
|
||||
resolvers::kv::{KVEventContent, KVResolver},
|
||||
room::Room,
|
||||
store::memory::MemoryStore,
|
||||
};
|
||||
use iced::{
|
||||
keyboard::KeyCode,
|
||||
|
@ -16,13 +16,11 @@ use iced::{
|
|||
};
|
||||
|
||||
struct Flags {
|
||||
room: Room<KVResolver>,
|
||||
secret: ActorSecret,
|
||||
state: State<KVResolver>,
|
||||
}
|
||||
|
||||
struct Counter {
|
||||
room: Mutex<Room<KVResolver>>,
|
||||
secret: ActorSecret,
|
||||
struct Main {
|
||||
state: Mutex<State<KVResolver>>,
|
||||
key: String,
|
||||
value: String,
|
||||
key_id: text_input::Id,
|
||||
|
@ -39,17 +37,16 @@ enum Message {
|
|||
FocusPrev,
|
||||
}
|
||||
|
||||
impl Application for Counter {
|
||||
impl Application for Main {
|
||||
type Executor = iced::executor::Default;
|
||||
type Message = Message;
|
||||
type Theme = iced::Theme;
|
||||
type Flags = Flags;
|
||||
|
||||
fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) {
|
||||
let room = Mutex::new(flags.room);
|
||||
let state = Mutex::new(flags.state);
|
||||
let state = Self {
|
||||
room,
|
||||
secret: flags.secret,
|
||||
state,
|
||||
key: String::new(),
|
||||
value: String::new(),
|
||||
key_id: text_input::Id::unique(),
|
||||
|
@ -74,14 +71,11 @@ impl Application for Counter {
|
|||
|
||||
let key = std::mem::take(&mut self.key);
|
||||
let value = std::mem::take(&mut self.value);
|
||||
self.room
|
||||
self.state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.create_event(
|
||||
CoreContent::Custom(KVEventContent::Set(key, value)),
|
||||
&self.secret,
|
||||
)
|
||||
.unwrap();
|
||||
.create_event(CoreContent::Custom(KVEventContent::Set(key, value)))
|
||||
.unwrap()
|
||||
}
|
||||
Message::FocusNext => {
|
||||
let next = match &self.focused {
|
||||
|
@ -106,13 +100,14 @@ impl Application for Counter {
|
|||
}
|
||||
|
||||
fn view(&self) -> Element<'_, Self::Message> {
|
||||
let mut room = self.room.lock().unwrap();
|
||||
let store = MemoryStore::new()
|
||||
.init(room.get_resolver().get_state_config())
|
||||
.unwrap();
|
||||
let state = room.resolve_state(*store);
|
||||
dbg!(&state);
|
||||
let rows = state.table("kv").unwrap().all().unwrap();
|
||||
let mut state = self.state.lock().unwrap();
|
||||
|
||||
fn resolve<R: Resolver>(state: &mut State<R>) {
|
||||
state.room.resolve_state(&mut *state.store);
|
||||
}
|
||||
|
||||
resolve(&mut state);
|
||||
let rows = state.store.table("kv").unwrap().all().unwrap();
|
||||
let mut column = Column::new();
|
||||
column = column.push(text(format!("{} keys", rows.len())).size(24));
|
||||
for row in rows {
|
||||
|
@ -159,22 +154,105 @@ impl Application for Counter {
|
|||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (_actor, secret) = ActorId::new(SignatureType::Ed25519);
|
||||
let room = Room::builder()
|
||||
.with_hasher(HashType::Sha256)
|
||||
.with_signer(SignatureType::Ed25519)
|
||||
.with_resolver(KVResolver)
|
||||
.create(&secret)
|
||||
.unwrap();
|
||||
Counter::run(Settings {
|
||||
#[derive(Debug)]
|
||||
struct State<R: Resolver> {
|
||||
db: Rc<rusqlite::Connection>,
|
||||
room: Room<R>,
|
||||
secret: ActorSecret,
|
||||
store: Box<dag_resolve::store::sqlite::Database>,
|
||||
}
|
||||
|
||||
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"),
|
||||
flags: Flags { room, secret },
|
||||
flags: Flags { state },
|
||||
id: None,
|
||||
window: iced::window::Settings::default(),
|
||||
default_text_size: 16.0,
|
||||
antialiasing: false,
|
||||
exit_on_close_request: true,
|
||||
})
|
||||
.unwrap();
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -65,15 +65,15 @@ impl<R: Resolver> Room<R> {
|
|||
&self.resolver
|
||||
}
|
||||
|
||||
pub fn resolve_state<S>(&mut self, initial_state: S) -> S
|
||||
pub fn resolve_state<S>(&self, state: &mut S)
|
||||
where
|
||||
S: proto::table::State,
|
||||
{
|
||||
let resolver = self.get_resolver();
|
||||
let sorted = sort(|a, b| resolver.tiebreak(a, b), &self.events);
|
||||
let state = initial_state;
|
||||
state.reset().unwrap();
|
||||
for event in sorted {
|
||||
let effects = resolver.resolve(&state, event);
|
||||
let effects = resolver.resolve(state, event);
|
||||
for effect in effects {
|
||||
match effect {
|
||||
ResolverEffect::Insert { table, row } => {
|
||||
|
@ -82,7 +82,6 @@ impl<R: Resolver> Room<R> {
|
|||
}
|
||||
}
|
||||
}
|
||||
state
|
||||
}
|
||||
|
||||
pub fn create_event(
|
||||
|
|
|
@ -109,7 +109,11 @@ pub trait State: Debug {
|
|||
type Err: Debug;
|
||||
type Table: Table;
|
||||
|
||||
/// Get a table
|
||||
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 {
|
||||
|
|
|
@ -40,6 +40,13 @@ impl State for MemoryStore {
|
|||
fn table(&self, name: &str) -> Result<Self::Table, Self::Err> {
|
||||
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 {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::{collections::HashMap, path::Path, rc::Rc};
|
||||
|
||||
use crate::proto::table;
|
||||
use crate::proto::table::{self, State};
|
||||
use rusqlite::params_from_iter;
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -39,8 +39,30 @@ impl Database {
|
|||
}
|
||||
|
||||
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();
|
||||
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
|
||||
sql.push_str("DROP TABLE IF EXISTS '");
|
||||
sql.push_str(&table_name.replace('\'', "''"));
|
||||
|
@ -64,24 +86,7 @@ impl Database {
|
|||
sql.push_str(");\n");
|
||||
}
|
||||
self.connection.execute_batch(&sql).unwrap();
|
||||
self.config = Some(config);
|
||||
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(),
|
||||
})
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,7 +94,7 @@ impl table::Table for Table {
|
|||
type Err = Error;
|
||||
|
||||
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('\'');
|
||||
let mut sql_query = self.connection.prepare(&sql).unwrap();
|
||||
|
|
Loading…
Reference in a new issue