Use Garmelon/vault

This commit is contained in:
Joscha 2023-02-12 23:09:59 +01:00
parent 35a140e21f
commit 7e9e441c1e
15 changed files with 442 additions and 458 deletions

10
Cargo.lock generated
View file

@ -203,6 +203,7 @@ dependencies = [
"toss", "toss",
"unicode-segmentation", "unicode-segmentation",
"unicode-width", "unicode-width",
"vault",
] ]
[[package]] [[package]]
@ -1392,6 +1393,15 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "vault"
version = "0.1.0"
source = "git+https://github.com/Garmelon/vault.git?tag=v0.1.0#028c72cac4e84bfbbf9fb03b15acb59989a31df9"
dependencies = [
"rusqlite",
"tokio",
]
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"

View file

@ -47,3 +47,11 @@ rev = "0d59116012a51516a821991e2969b1cf4779770f"
# [patch."https://github.com/Garmelon/toss.git"] # [patch."https://github.com/Garmelon/toss.git"]
# toss = { path = "../toss/" } # toss = { path = "../toss/" }
[dependencies.vault]
git = "https://github.com/Garmelon/vault.git"
tag = "v0.1.0"
features = ["tokio"]
# [patch."https://github.com/Garmelon/vault.git"]
# vault = { path = "../vault/" }

View file

@ -14,7 +14,7 @@ use log::{debug, error, info, warn};
use tokio::select; use tokio::select;
use tokio::sync::oneshot; use tokio::sync::oneshot;
use crate::macros::ok_or_return; use crate::macros::{logging_unwrap, ok_or_return};
use crate::vault::EuphRoomVault; use crate::vault::EuphRoomVault;
const LOG_INTERVAL: Duration = Duration::from_secs(10); const LOG_INTERVAL: Duration = Duration::from_secs(10);
@ -93,7 +93,7 @@ impl Room {
self.state.conn_tx().ok_or(Error::NotConnected) self.state.conn_tx().ok_or(Error::NotConnected)
} }
pub fn handle_event(&mut self, event: Event) { pub async fn handle_event(&mut self, event: Event) {
match event { match event {
Event::Connecting(_) => { Event::Connecting(_) => {
self.state = State::Connecting; self.state = State::Connecting;
@ -121,11 +121,11 @@ impl Room {
let cookies = &*self.instance.config().server.cookies; let cookies = &*self.instance.config().server.cookies;
let cookies = cookies.lock().unwrap().clone(); let cookies = cookies.lock().unwrap().clone();
self.vault.vault().set_cookies(cookies); logging_unwrap!(self.vault.vault().set_cookies(cookies).await);
} }
Event::Packet(_, packet, Snapshot { conn_tx, state }) => { Event::Packet(_, packet, Snapshot { conn_tx, state }) => {
self.state = State::Connected(conn_tx, state); self.state = State::Connected(conn_tx, state);
self.on_packet(packet); self.on_packet(packet).await;
} }
Event::Disconnected(_) => { Event::Disconnected(_) => {
self.state = State::Disconnected; self.state = State::Disconnected;
@ -173,7 +173,7 @@ impl Room {
} }
async fn request_logs(vault: &EuphRoomVault, conn_tx: &ConnTx) { async fn request_logs(vault: &EuphRoomVault, conn_tx: &ConnTx) {
let before = match vault.last_span().await { let before = match logging_unwrap!(vault.last_span().await) {
Some((None, _)) => return, // Already at top of room history Some((None, _)) => return, // Already at top of room history
Some((Some(before), _)) => Some(before), Some((Some(before), _)) => Some(before),
None => None, None => None,
@ -203,7 +203,7 @@ impl Room {
} }
} }
fn on_packet(&mut self, packet: ParsedPacket) { async fn on_packet(&mut self, packet: ParsedPacket) {
let instance_name = &self.instance.config().name; let instance_name = &self.instance.config().name;
let data = ok_or_return!(&packet.content); let data = ok_or_return!(&packet.content);
match data { match data {
@ -238,26 +238,39 @@ impl Room {
Data::SendEvent(SendEvent(msg)) => { Data::SendEvent(SendEvent(msg)) => {
let own_user_id = self.own_user_id(); let own_user_id = self.own_user_id();
if let Some(last_msg_id) = &mut self.last_msg_id { if let Some(last_msg_id) = &mut self.last_msg_id {
logging_unwrap!(
self.vault self.vault
.add_msg(Box::new(msg.clone()), *last_msg_id, own_user_id); .add_msg(Box::new(msg.clone()), *last_msg_id, own_user_id)
.await
);
*last_msg_id = Some(msg.id); *last_msg_id = Some(msg.id);
} }
} }
Data::SnapshotEvent(d) => { Data::SnapshotEvent(d) => {
info!("{instance_name}: successfully joined"); info!("{instance_name}: successfully joined");
self.vault.join(Time::now()); logging_unwrap!(self.vault.join(Time::now()).await);
self.last_msg_id = Some(d.log.last().map(|m| m.id)); self.last_msg_id = Some(d.log.last().map(|m| m.id));
self.vault.add_msgs(d.log.clone(), None, self.own_user_id()); logging_unwrap!(
self.vault
.add_msgs(d.log.clone(), None, self.own_user_id())
.await
);
} }
Data::LogReply(d) => { Data::LogReply(d) => {
logging_unwrap!(
self.vault self.vault
.add_msgs(d.log.clone(), d.before, self.own_user_id()); .add_msgs(d.log.clone(), d.before, self.own_user_id())
.await
);
} }
Data::SendReply(SendReply(msg)) => { Data::SendReply(SendReply(msg)) => {
let own_user_id = self.own_user_id(); let own_user_id = self.own_user_id();
if let Some(last_msg_id) = &mut self.last_msg_id { if let Some(last_msg_id) = &mut self.last_msg_id {
logging_unwrap!(
self.vault self.vault
.add_msg(Box::new(msg.clone()), *last_msg_id, own_user_id); .add_msg(Box::new(msg.clone()), *last_msg_id, own_user_id)
.await
);
*last_msg_id = Some(msg.id); *last_msg_id = Some(msg.id);
} }
} }

View file

@ -85,7 +85,7 @@ pub async fn export(vault: &EuphVault, mut args: Args) -> anyhow::Result<()> {
} }
let rooms = if args.all { let rooms = if args.all {
let mut rooms = vault.rooms().await; let mut rooms = vault.rooms().await?;
rooms.sort_unstable(); rooms.sort_unstable();
rooms rooms
} else { } else {

View file

@ -10,7 +10,7 @@ pub async fn export<W: Write>(vault: &EuphRoomVault, file: &mut W) -> anyhow::Re
let mut total = 0; let mut total = 0;
let mut offset = 0; let mut offset = 0;
loop { loop {
let messages = vault.chunk_at_offset(CHUNK_SIZE, offset).await; let messages = vault.chunk_at_offset(CHUNK_SIZE, offset).await?;
offset += messages.len(); offset += messages.len();
if messages.is_empty() { if messages.is_empty() {
@ -42,7 +42,7 @@ pub async fn export_stream<W: Write>(vault: &EuphRoomVault, file: &mut W) -> any
let mut total = 0; let mut total = 0;
let mut offset = 0; let mut offset = 0;
loop { loop {
let messages = vault.chunk_at_offset(CHUNK_SIZE, offset).await; let messages = vault.chunk_at_offset(CHUNK_SIZE, offset).await?;
offset += messages.len(); offset += messages.len();
if messages.is_empty() { if messages.is_empty() {

View file

@ -16,11 +16,11 @@ const TIME_EMPTY: &str = " ";
pub async fn export<W: Write>(vault: &EuphRoomVault, out: &mut W) -> anyhow::Result<()> { pub async fn export<W: Write>(vault: &EuphRoomVault, out: &mut W) -> anyhow::Result<()> {
let mut exported_trees = 0; let mut exported_trees = 0;
let mut exported_msgs = 0; let mut exported_msgs = 0;
let mut root_id = vault.first_root_id().await; let mut root_id = vault.first_root_id().await?;
while let Some(some_root_id) = root_id { while let Some(some_root_id) = root_id {
let tree = vault.tree(some_root_id).await; let tree = vault.tree(some_root_id).await?;
write_tree(out, &tree, some_root_id, 0)?; write_tree(out, &tree, some_root_id, 0)?;
root_id = vault.next_root_id(some_root_id).await; root_id = vault.next_root_id(some_root_id).await?;
exported_trees += 1; exported_trees += 1;
exported_msgs += tree.len(); exported_msgs += tree.len();

View file

@ -29,3 +29,17 @@ macro_rules! ok_or_return {
}; };
} }
pub(crate) use ok_or_return; pub(crate) use ok_or_return;
// TODO Get rid of this macro as much as possible
macro_rules! logging_unwrap {
($e:expr) => {
match $e {
Ok(value) => value,
Err(err) => {
log::error!("{err}");
panic!("{err}");
}
}
};
}
pub(crate) use logging_unwrap;

View file

@ -149,11 +149,11 @@ async fn main() -> anyhow::Result<()> {
Command::Gc => { Command::Gc => {
eprintln!("Cleaning up and compacting vault"); eprintln!("Cleaning up and compacting vault");
eprintln!("This may take a while..."); eprintln!("This may take a while...");
vault.gc().await; vault.gc().await?;
} }
Command::ClearCookies => { Command::ClearCookies => {
eprintln!("Clearing cookies"); eprintln!("Clearing cookies");
vault.euph().set_cookies(CookieJar::new()); vault.euph().set_cookies(CookieJar::new()).await?;
} }
} }

View file

@ -10,7 +10,6 @@ use std::io;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use log::error;
use parking_lot::FairMutex; use parking_lot::FairMutex;
use tokio::sync::mpsc::error::TryRecvError; use tokio::sync::mpsc::error::TryRecvError;
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
@ -19,7 +18,7 @@ use toss::terminal::Terminal;
use crate::config::Config; use crate::config::Config;
use crate::logger::{LogMsg, Logger}; use crate::logger::{LogMsg, Logger};
use crate::macros::{ok_or_return, some_or_return}; use crate::macros::{logging_unwrap, ok_or_return, some_or_return};
use crate::vault::Vault; use crate::vault::Vault;
pub use self::chat::ChatMsg; pub use self::chat::ChatMsg;
@ -231,7 +230,7 @@ impl Ui {
.await .await
} }
UiEvent::Euph(event) => { UiEvent::Euph(event) => {
if self.rooms.handle_euph_event(event) { if self.rooms.handle_euph_event(event).await {
EventHandleResult::Redraw EventHandleResult::Redraw
} else { } else {
EventHandleResult::Continue EventHandleResult::Continue
@ -288,17 +287,11 @@ impl Ui {
.await .await
} }
Mode::Log => { Mode::Log => {
let reaction = match self let reaction = self
.log_chat .log_chat
.handle_input_event(terminal, crossterm_lock, &event, false) .handle_input_event(terminal, crossterm_lock, &event, false)
.await .await;
{ let reaction = logging_unwrap!(reaction);
Ok(reaction) => reaction,
Err(err) => {
error!("{err}");
panic!("{err}");
}
};
reaction.handled() reaction.handled()
} }
}; };

View file

@ -10,12 +10,12 @@ use std::fmt;
use std::sync::Arc; use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use log::error;
use parking_lot::FairMutex; use parking_lot::FairMutex;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use toss::frame::{Frame, Pos, Size}; use toss::frame::{Frame, Pos, Size};
use toss::terminal::Terminal; use toss::terminal::Terminal;
use crate::macros::logging_unwrap;
use crate::store::{Msg, MsgStore}; use crate::store::{Msg, MsgStore};
use crate::ui::input::{key, InputEvent, KeyBindingsList}; use crate::ui::input::{key, InputEvent, KeyBindingsList};
use crate::ui::util; use crate::ui::util;
@ -439,13 +439,7 @@ where
async fn render(self: Box<Self>, frame: &mut Frame) { async fn render(self: Box<Self>, frame: &mut Frame) {
let mut guard = self.inner.lock().await; let mut guard = self.inner.lock().await;
let blocks = match guard.relayout(self.nick, self.focused, frame).await { let blocks = logging_unwrap!(guard.relayout(self.nick, self.focused, frame).await);
Ok(blocks) => blocks,
Err(err) => {
error!("{err}");
panic!("{err}");
}
};
let size = frame.size(); let size = frame.size();
for block in blocks.into_blocks().blocks { for block in blocks.into_blocks().blocks {

View file

@ -5,7 +5,6 @@ use crossterm::style::{ContentStyle, Stylize};
use euphoxide::api::{Data, Message, MessageId, PacketType, SessionId}; use euphoxide::api::{Data, Message, MessageId, PacketType, SessionId};
use euphoxide::bot::instance::{Event, ServerConfig}; use euphoxide::bot::instance::{Event, ServerConfig};
use euphoxide::conn::{self, Joined, Joining, SessionInfo}; use euphoxide::conn::{self, Joined, Joining, SessionInfo};
use log::error;
use parking_lot::FairMutex; use parking_lot::FairMutex;
use tokio::sync::oneshot::error::TryRecvError; use tokio::sync::oneshot::error::TryRecvError;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::{mpsc, oneshot};
@ -14,6 +13,7 @@ use toss::terminal::Terminal;
use crate::config; use crate::config;
use crate::euph; use crate::euph;
use crate::macros::logging_unwrap;
use crate::ui::chat::{ChatState, Reaction}; use crate::ui::chat::{ChatState, Reaction};
use crate::ui::input::{key, InputEvent, KeyBindingsList}; use crate::ui::input::{key, InputEvent, KeyBindingsList};
use crate::ui::widgets::border::Border; use crate::ui::widgets::border::Border;
@ -143,7 +143,7 @@ impl EuphRoom {
} }
pub async fn unseen_msgs_count(&self) -> usize { pub async fn unseen_msgs_count(&self) -> usize {
self.vault().unseen_msgs_count().await logging_unwrap!(self.vault().unseen_msgs_count().await)
} }
async fn stabilize_pseudo_msg(&mut self) { async fn stabilize_pseudo_msg(&mut self) {
@ -327,17 +327,11 @@ impl EuphRoom {
Some(euph::State::Connected(_, conn::State::Joined(_))) Some(euph::State::Connected(_, conn::State::Joined(_)))
); );
let reaction = match self let reaction = self
.chat .chat
.handle_input_event(terminal, crossterm_lock, event, can_compose) .handle_input_event(terminal, crossterm_lock, event, can_compose)
.await .await;
{ let reaction = logging_unwrap!(reaction);
Ok(reaction) => reaction,
Err(err) => {
error!("{err}");
panic!("{err}");
}
};
match reaction { match reaction {
Reaction::NotHandled => {} Reaction::NotHandled => {}
@ -434,7 +428,7 @@ impl EuphRoom {
match event { match event {
key!('i') => { key!('i') => {
if let Some(id) = self.chat.cursor().await { if let Some(id) = self.chat.cursor().await {
if let Some(msg) = self.vault().full_msg(id).await { if let Some(msg) = logging_unwrap!(self.vault().full_msg(id).await) {
self.state = State::InspectMessage(msg); self.state = State::InspectMessage(msg);
} }
} }
@ -442,7 +436,7 @@ impl EuphRoom {
} }
key!('I') => { key!('I') => {
if let Some(id) = self.chat.cursor().await { if let Some(id) = self.chat.cursor().await {
if let Some(msg) = self.vault().msg(id).await { if let Some(msg) = logging_unwrap!(self.vault().msg(id).await) {
self.state = State::Links(LinksState::new(&msg.content)); self.state = State::Links(LinksState::new(&msg.content));
} }
} }
@ -679,7 +673,7 @@ impl EuphRoom {
} }
} }
pub fn handle_event(&mut self, event: Event) -> bool { pub async fn handle_event(&mut self, event: Event) -> bool {
let handled = if self.room.is_some() { let handled = if self.room.is_some() {
if let Event::Packet(_, packet, _) = &event { if let Event::Packet(_, packet, _) = &event {
match &packet.content { match &packet.content {
@ -694,7 +688,7 @@ impl EuphRoom {
}; };
if let Some(room) = &mut self.room { if let Some(room) = &mut self.room {
room.handle_event(event); room.handle_event(event).await;
} }
handled handled

View file

@ -13,6 +13,7 @@ use toss::terminal::Terminal;
use crate::config::{Config, RoomsSortOrder}; use crate::config::{Config, RoomsSortOrder};
use crate::euph; use crate::euph;
use crate::macros::logging_unwrap;
use crate::vault::Vault; use crate::vault::Vault;
use super::euph::room::EuphRoom; use super::euph::room::EuphRoom;
@ -69,8 +70,8 @@ impl Rooms {
vault: Vault, vault: Vault,
ui_event_tx: mpsc::UnboundedSender<UiEvent>, ui_event_tx: mpsc::UnboundedSender<UiEvent>,
) -> Self { ) -> Self {
let euph_server_config = let cookies = logging_unwrap!(vault.euph().cookies().await);
ServerConfig::default().cookies(Arc::new(Mutex::new(vault.euph().cookies().await))); let euph_server_config = ServerConfig::default().cookies(Arc::new(Mutex::new(cookies)));
let mut result = Self { let mut result = Self {
config, config,
@ -112,13 +113,8 @@ impl Rooms {
/// - failed connection attempts, or /// - failed connection attempts, or
/// - rooms that were deleted from the db. /// - rooms that were deleted from the db.
async fn stabilize_rooms(&mut self) { async fn stabilize_rooms(&mut self) {
let mut rooms_set = self let rooms = logging_unwrap!(self.vault.euph().rooms().await);
.vault let mut rooms_set = rooms.into_iter().collect::<HashSet<_>>();
.euph()
.rooms()
.await
.into_iter()
.collect::<HashSet<_>>();
// Prevent room that is currently being shown from being removed. This // Prevent room that is currently being shown from being removed. This
// could otherwise happen when connecting to a room that doesn't exist. // could otherwise happen when connecting to a room that doesn't exist.
@ -533,7 +529,7 @@ impl Rooms {
} }
key!(Enter) if editor.text() == *name => { key!(Enter) if editor.text() == *name => {
self.euph_rooms.remove(name); self.euph_rooms.remove(name);
self.vault.euph().delete(name.clone()); logging_unwrap!(self.vault.euph().room(name.clone()).delete().await);
self.state = State::ShowList; self.state = State::ShowList;
return true; return true;
} }
@ -548,10 +544,10 @@ impl Rooms {
false false
} }
pub fn handle_euph_event(&mut self, event: Event) -> bool { pub async fn handle_euph_event(&mut self, event: Event) -> bool {
let instance_name = event.config().name.clone(); let instance_name = event.config().name.clone();
let room = self.get_or_insert_room(instance_name.clone()); let room = self.get_or_insert_room(instance_name.clone());
let handled = room.handle_event(event); let handled = room.handle_event(event).await;
let room_visible = match &self.state { let room_visible = match &self.state {
State::ShowRoom(name) => *name == instance_name, State::ShowRoom(name) => *name == instance_name,

View file

@ -2,43 +2,42 @@ mod euph;
mod migrate; mod migrate;
mod prepare; mod prepare;
use std::fs;
use std::path::Path; use std::path::Path;
use std::{fs, thread};
use log::error;
use rusqlite::Connection; use rusqlite::Connection;
use tokio::sync::{mpsc, oneshot}; use vault::tokio::TokioVault;
use vault::Action;
use self::euph::EuphRequest;
pub use self::euph::{EuphRoomVault, EuphVault}; pub use self::euph::{EuphRoomVault, EuphVault};
enum Request {
Close(oneshot::Sender<()>),
Gc(oneshot::Sender<()>),
Euph(EuphRequest),
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Vault { pub struct Vault {
tx: mpsc::UnboundedSender<Request>, tokio_vault: TokioVault,
ephemeral: bool, ephemeral: bool,
} }
struct GcAction;
impl Action for GcAction {
type Result = ();
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
conn.execute_batch("ANALYZE; VACUUM;")
}
}
impl Vault { impl Vault {
pub fn ephemeral(&self) -> bool { pub fn ephemeral(&self) -> bool {
self.ephemeral self.ephemeral
} }
pub async fn close(&self) { pub async fn close(&self) {
let (tx, rx) = oneshot::channel(); self.tokio_vault.stop().await;
let _ = self.tx.send(Request::Close(tx));
let _ = rx.await;
} }
pub async fn gc(&self) { pub async fn gc(&self) -> vault::tokio::Result<()> {
let (tx, rx) = oneshot::channel(); self.tokio_vault.execute(GcAction).await
let _ = self.tx.send(Request::Gc(tx));
let _ = rx.await;
} }
pub fn euph(&self) -> EuphVault { pub fn euph(&self) -> EuphVault {
@ -46,47 +45,17 @@ impl Vault {
} }
} }
fn run(mut conn: Connection, mut rx: mpsc::UnboundedReceiver<Request>) { fn launch_from_connection(conn: Connection, ephemeral: bool) -> rusqlite::Result<Vault> {
while let Some(request) = rx.blocking_recv() {
match request {
Request::Close(tx) => {
eprintln!("Closing vault");
if let Err(e) = conn.execute_batch("PRAGMA optimize") {
error!("{e}");
}
// Ensure `Vault::close` exits only after the sqlite connection
// has been closed properly.
drop(conn);
drop(tx);
break;
}
Request::Gc(tx) => {
if let Err(e) = conn.execute_batch("ANALYZE; VACUUM;") {
error!("{e}");
}
drop(tx);
}
Request::Euph(r) => {
if let Err(e) = r.perform(&mut conn) {
error!("{e}");
}
}
}
}
}
fn launch_from_connection(mut conn: Connection, ephemeral: bool) -> rusqlite::Result<Vault> {
conn.pragma_update(None, "foreign_keys", true)?; conn.pragma_update(None, "foreign_keys", true)?;
conn.pragma_update(None, "trusted_schema", false)?; conn.pragma_update(None, "trusted_schema", false)?;
eprintln!("Opening vault"); eprintln!("Opening vault");
migrate::migrate(&mut conn)?; let tokio_vault = TokioVault::launch_and_prepare(conn, &migrate::MIGRATIONS, prepare::prepare)?;
prepare::prepare(&mut conn)?; Ok(Vault {
tokio_vault,
let (tx, rx) = mpsc::unbounded_channel(); ephemeral,
thread::spawn(move || run(conn, rx)); })
Ok(Vault { tx, ephemeral })
} }
pub fn launch(path: &Path) -> rusqlite::Result<Vault> { pub fn launch(path: &Path) -> rusqlite::Result<Vault> {

View file

@ -1,4 +1,3 @@
use std::convert::Infallible;
use std::mem; use std::mem;
use std::str::FromStr; use std::str::FromStr;
@ -8,11 +7,15 @@ use euphoxide::api::{Message, MessageId, SessionId, SessionView, Snowflake, Time
use rusqlite::types::{FromSql, FromSqlError, ToSqlOutput, Value, ValueRef}; use rusqlite::types::{FromSql, FromSqlError, ToSqlOutput, Value, ValueRef};
use rusqlite::{named_params, params, Connection, OptionalExtension, ToSql, Transaction}; use rusqlite::{named_params, params, Connection, OptionalExtension, ToSql, Transaction};
use time::OffsetDateTime; use time::OffsetDateTime;
use tokio::sync::oneshot; use vault::Action;
use crate::euph::SmallMessage; use crate::euph::SmallMessage;
use crate::store::{MsgStore, Path, Tree}; use crate::store::{MsgStore, Path, Tree};
///////////////////
// Wrapper types //
///////////////////
/// Wrapper for [`Snowflake`] that implements useful rusqlite traits. /// Wrapper for [`Snowflake`] that implements useful rusqlite traits.
struct WSnowflake(Snowflake); struct WSnowflake(Snowflake);
@ -47,6 +50,10 @@ impl FromSql for WTime {
} }
} }
///////////////
// EuphVault //
///////////////
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EuphVault { pub struct EuphVault {
vault: super::Vault, vault: super::Vault,
@ -69,216 +76,36 @@ impl EuphVault {
} }
} }
#[derive(Debug, Clone)] macro_rules! euph_vault_actions {
pub struct EuphRoomVault {
vault: EuphVault,
room: String,
}
impl EuphRoomVault {
pub fn vault(&self) -> &EuphVault {
&self.vault
}
pub fn room(&self) -> &str {
&self.room
}
}
#[async_trait]
impl MsgStore<SmallMessage> for EuphRoomVault {
type Error = Infallible;
async fn path(&self, id: &MessageId) -> Result<Path<MessageId>, Self::Error> {
Ok(self.path(*id).await)
}
async fn msg(&self, id: &MessageId) -> Result<Option<SmallMessage>, Self::Error> {
Ok(self.msg(*id).await)
}
async fn tree(&self, root_id: &MessageId) -> Result<Tree<SmallMessage>, Self::Error> {
Ok(self.tree(*root_id).await)
}
async fn first_root_id(&self) -> Result<Option<MessageId>, Self::Error> {
Ok(self.first_root_id().await)
}
async fn last_root_id(&self) -> Result<Option<MessageId>, Self::Error> {
Ok(self.last_root_id().await)
}
async fn prev_root_id(&self, root_id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
Ok(self.prev_root_id(*root_id).await)
}
async fn next_root_id(&self, root_id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
Ok(self.next_root_id(*root_id).await)
}
async fn oldest_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
Ok(self.oldest_msg_id().await)
}
async fn newest_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
Ok(self.newest_msg_id().await)
}
async fn older_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
Ok(self.older_msg_id(*id).await)
}
async fn newer_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
Ok(self.newer_msg_id(*id).await)
}
async fn oldest_unseen_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
Ok(self.oldest_unseen_msg_id().await)
}
async fn newest_unseen_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
Ok(self.newest_unseen_msg_id().await)
}
async fn older_unseen_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
Ok(self.older_unseen_msg_id(*id).await)
}
async fn newer_unseen_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
Ok(self.newer_unseen_msg_id(*id).await)
}
async fn unseen_msgs_count(&self) -> Result<usize, Self::Error> {
Ok(self.unseen_msgs_count().await)
}
async fn set_seen(&self, id: &MessageId, seen: bool) -> Result<(), Self::Error> {
self.set_seen(*id, seen);
Ok(())
}
async fn set_older_seen(&self, id: &MessageId, seen: bool) -> Result<(), Self::Error> {
self.set_older_seen(*id, seen);
Ok(())
}
}
trait Request {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()>;
}
macro_rules! requests_vault_fn {
( $var:ident : $fn:ident( $( $arg:ident : $ty:ty ),* ) ) => {
pub fn $fn(&self $( , $arg: $ty )* ) {
let request = EuphRequest::$var($var { $( $arg, )* });
let _ = self.vault.tx.send(super::Request::Euph(request));
}
};
( $var:ident : $fn:ident( $( $arg:ident : $ty:ty ),* ) -> $res:ty ) => {
pub async fn $fn(&self $( , $arg: $ty )* ) -> $res {
let (tx, rx) = oneshot::channel();
let request = EuphRequest::$var($var {
$( $arg, )*
result: tx,
});
let _ = self.vault.tx.send(super::Request::Euph(request));
rx.await.unwrap()
}
};
}
// This doesn't match the type of the `room` argument because that's apparently
// impossible to match to `String`. See also the readme of
// https://github.com/danielhenrymantilla/rust-defile for a description of this
// phenomenon and some examples.
macro_rules! requests_room_vault_fn {
( $fn:ident ( room: $mustbestring:ty $( , $arg:ident : $ty:ty )* ) ) => {
pub fn $fn(&self $( , $arg: $ty )* ) {
self.vault.$fn(self.room.clone() $( , $arg )* );
}
};
( $fn:ident ( room: $mustbestring:ty $( , $arg:ident : $ty:ty )* ) -> $res:ty ) => {
pub async fn $fn(&self $( , $arg: $ty )* ) -> $res {
self.vault.$fn(self.room.clone() $( , $arg )* ).await
}
};
( $( $tt:tt )* ) => { };
}
macro_rules! requests {
( $( ( $(
$var:ident : $fn:ident ( $( $arg:ident : $ty:ty ),* ) $( -> $res:ty )? ; $struct:ident : $fn:ident ( $( $arg:ident : $arg_ty:ty ),* ) -> $res:ty ;
)* ) => { )* ) => {
$( $(
pub(super) struct $var { struct $struct {
$( $arg: $ty, )* $( $arg: $arg_ty, )*
$( result: oneshot::Sender<$res>, )?
} }
)* )*
pub(super) enum EuphRequest {
$( $var($var), )*
}
impl EuphRequest {
pub(super) fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> {
match self {
$( Self::$var(request) => request.perform(conn), )*
}
}
}
#[allow(dead_code)]
impl EuphVault { impl EuphVault {
$( requests_vault_fn!($var : $fn( $( $arg: $ty ),* ) $( -> $res )? ); )* $(
pub async fn $fn(&self, $( $arg: $arg_ty, )* ) -> vault::tokio::Result<$res> {
self.vault.tokio_vault.execute($struct { $( $arg, )* }).await
} }
)*
#[allow(dead_code)]
impl EuphRoomVault {
$( requests_room_vault_fn!($fn( $( $arg: $ty ),* ) $( -> $res )? ); )*
} }
}; };
} }
requests! { euph_vault_actions! {
// Cookies
GetCookies : cookies() -> CookieJar; GetCookies : cookies() -> CookieJar;
SetCookies : set_cookies(cookies: CookieJar); SetCookies : set_cookies(cookies: CookieJar) -> ();
// Rooms
GetRooms : rooms() -> Vec<String>; GetRooms : rooms() -> Vec<String>;
Join : join(room: String, time: Time);
Delete : delete(room: String);
// Message
AddMsg : add_msg(room: String, msg: Box<Message>, prev_msg_id: Option<MessageId>, own_user_id: Option<UserId>);
AddMsgs : add_msgs(room: String, msgs: Vec<Message>, next_msg_id: Option<MessageId>, own_user_id: Option<UserId>);
GetLastSpan : last_span(room: String) -> Option<(Option<MessageId>, Option<MessageId>)>;
GetPath : path(room: String, id: MessageId) -> Path<MessageId>;
GetMsg : msg(room: String, id: MessageId) -> Option<SmallMessage>;
GetFullMsg : full_msg(room: String, id: MessageId) -> Option<Message>;
GetTree : tree(room: String, root_id: MessageId) -> Tree<SmallMessage>;
GetFirstRootId : first_root_id(room: String) -> Option<MessageId>;
GetLastRootId : last_root_id(room: String) -> Option<MessageId>;
GetPrevRootId : prev_root_id(room: String, root_id: MessageId) -> Option<MessageId>;
GetNextRootId : next_root_id(room: String, root_id: MessageId) -> Option<MessageId>;
GetOldestMsgId : oldest_msg_id(room: String) -> Option<MessageId>;
GetNewestMsgId : newest_msg_id(room: String) -> Option<MessageId>;
GetOlderMsgId : older_msg_id(room: String, id: MessageId) -> Option<MessageId>;
GetNewerMsgId : newer_msg_id(room: String, id: MessageId) -> Option<MessageId>;
GetOldestUnseenMsgId : oldest_unseen_msg_id(room: String) -> Option<MessageId>;
GetNewestUnseenMsgId : newest_unseen_msg_id(room: String) -> Option<MessageId>;
GetOlderUnseenMsgId : older_unseen_msg_id(room: String, id: MessageId) -> Option<MessageId>;
GetNewerUnseenMsgId : newer_unseen_msg_id(room: String, id: MessageId) -> Option<MessageId>;
GetUnseenMsgsCount : unseen_msgs_count(room: String) -> usize;
SetSeen : set_seen(room: String, id: MessageId, seen: bool);
SetOlderSeen : set_older_seen(room: String, id: MessageId, seen: bool);
GetChunkAtOffset : chunk_at_offset(room: String, amount: usize, offset: usize) -> Vec<Message>;
} }
impl Request for GetCookies { impl Action for GetCookies {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = CookieJar;
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let cookies = conn let cookies = conn
.prepare( .prepare(
" "
@ -296,14 +123,14 @@ impl Request for GetCookies {
for cookie in cookies { for cookie in cookies {
cookie_jar.add_original(cookie); cookie_jar.add_original(cookie);
} }
Ok(cookie_jar)
let _ = self.result.send(cookie_jar);
Ok(())
} }
} }
impl Request for SetCookies { impl Action for SetCookies {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = ();
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let tx = conn.transaction()?; let tx = conn.transaction()?;
// Since euphoria sets all cookies on every response, we can just delete // Since euphoria sets all cookies on every response, we can just delete
@ -326,24 +153,100 @@ impl Request for SetCookies {
} }
} }
impl Request for GetRooms { impl Action for GetRooms {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Vec<String>;
let rooms = conn
.prepare( fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
conn.prepare(
" "
SELECT room SELECT room
FROM euph_rooms FROM euph_rooms
", ",
)? )?
.query_map([], |row| row.get(0))? .query_map([], |row| row.get(0))?
.collect::<rusqlite::Result<_>>()?; .collect::<rusqlite::Result<_>>()
let _ = self.result.send(rooms);
Ok(())
} }
} }
impl Request for Join { ///////////////////
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { // EuphRoomVault //
///////////////////
#[derive(Debug, Clone)]
pub struct EuphRoomVault {
vault: EuphVault,
room: String,
}
impl EuphRoomVault {
pub fn vault(&self) -> &EuphVault {
&self.vault
}
pub fn room(&self) -> &str {
&self.room
}
}
macro_rules! euph_room_vault_actions {
( $(
$struct:ident : $fn:ident ( $( $arg:ident : $arg_ty:ty ),* ) -> $res:ty ;
)* ) => {
$(
struct $struct {
room: String,
$( $arg: $arg_ty, )*
}
)*
impl EuphRoomVault {
$(
pub async fn $fn(&self, $( $arg: $arg_ty, )* ) -> vault::tokio::Result<$res> {
self.vault.vault.tokio_vault.execute($struct {
room: self.room.clone(),
$( $arg, )*
}).await
}
)*
}
};
}
euph_room_vault_actions! {
// Room
Join : join(time: Time) -> ();
Delete : delete() -> ();
// Message
AddMsg : add_msg(msg: Box<Message>, prev_msg_id: Option<MessageId>, own_user_id: Option<UserId>) -> ();
AddMsgs : add_msgs(msgs: Vec<Message>, next_msg_id: Option<MessageId>, own_user_id: Option<UserId>) -> ();
GetLastSpan : last_span() -> Option<(Option<MessageId>, Option<MessageId>)>;
GetPath : path(id: MessageId) -> Path<MessageId>;
GetMsg : msg(id: MessageId) -> Option<SmallMessage>;
GetFullMsg : full_msg(id: MessageId) -> Option<Message>;
GetTree : tree(root_id: MessageId) -> Tree<SmallMessage>;
GetFirstRootId : first_root_id() -> Option<MessageId>;
GetLastRootId : last_root_id() -> Option<MessageId>;
GetPrevRootId : prev_root_id(root_id: MessageId) -> Option<MessageId>;
GetNextRootId : next_root_id(root_id: MessageId) -> Option<MessageId>;
GetOldestMsgId : oldest_msg_id() -> Option<MessageId>;
GetNewestMsgId : newest_msg_id() -> Option<MessageId>;
GetOlderMsgId : older_msg_id(id: MessageId) -> Option<MessageId>;
GetNewerMsgId : newer_msg_id(id: MessageId) -> Option<MessageId>;
GetOldestUnseenMsgId : oldest_unseen_msg_id() -> Option<MessageId>;
GetNewestUnseenMsgId : newest_unseen_msg_id() -> Option<MessageId>;
GetOlderUnseenMsgId : older_unseen_msg_id(id: MessageId) -> Option<MessageId>;
GetNewerUnseenMsgId : newer_unseen_msg_id(id: MessageId) -> Option<MessageId>;
GetUnseenMsgsCount : unseen_msgs_count() -> usize;
SetSeen : set_seen(id: MessageId, seen: bool) -> ();
SetOlderSeen : set_older_seen(id: MessageId, seen: bool) -> ();
GetChunkAtOffset : chunk_at_offset(amount: usize, offset: usize) -> Vec<Message>;
}
impl Action for Join {
type Result = ();
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
conn.execute( conn.execute(
" "
INSERT INTO euph_rooms (room, first_joined, last_joined) INSERT INTO euph_rooms (room, first_joined, last_joined)
@ -357,8 +260,10 @@ impl Request for Join {
} }
} }
impl Request for Delete { impl Action for Delete {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = ();
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
conn.execute( conn.execute(
" "
DELETE FROM euph_rooms DELETE FROM euph_rooms
@ -525,8 +430,10 @@ fn add_span(
Ok(()) Ok(())
} }
impl Request for AddMsg { impl Action for AddMsg {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = ();
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let tx = conn.transaction()?; let tx = conn.transaction()?;
let end = self.msg.id; let end = self.msg.id;
@ -538,8 +445,10 @@ impl Request for AddMsg {
} }
} }
impl Request for AddMsgs { impl Action for AddMsgs {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = ();
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let tx = conn.transaction()?; let tx = conn.transaction()?;
if self.msgs.is_empty() { if self.msgs.is_empty() {
@ -559,8 +468,10 @@ impl Request for AddMsgs {
} }
} }
impl Request for GetLastSpan { impl Action for GetLastSpan {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<(Option<MessageId>, Option<MessageId>)>;
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let span = conn let span = conn
.prepare( .prepare(
" "
@ -578,13 +489,14 @@ impl Request for GetLastSpan {
)) ))
}) })
.optional()?; .optional()?;
let _ = self.result.send(span); Ok(span)
Ok(())
} }
} }
impl Request for GetPath { impl Action for GetPath {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Path<MessageId>;
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let path = conn let path = conn
.prepare( .prepare(
" "
@ -606,14 +518,14 @@ impl Request for GetPath {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
})? })?
.collect::<rusqlite::Result<_>>()?; .collect::<rusqlite::Result<_>>()?;
let path = Path::new(path); Ok(Path::new(path))
let _ = self.result.send(path);
Ok(())
} }
} }
impl Request for GetMsg { impl Action for GetMsg {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<SmallMessage>;
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let msg = conn let msg = conn
.query_row( .query_row(
" "
@ -635,13 +547,14 @@ impl Request for GetMsg {
}, },
) )
.optional()?; .optional()?;
let _ = self.result.send(msg); Ok(msg)
Ok(())
} }
} }
impl Request for GetFullMsg { impl Action for GetFullMsg {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<Message>;
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let mut query = conn.prepare( let mut query = conn.prepare(
" "
SELECT SELECT
@ -679,13 +592,14 @@ impl Request for GetFullMsg {
}) })
}) })
.optional()?; .optional()?;
let _ = self.result.send(msg); Ok(msg)
Ok(())
} }
} }
impl Request for GetTree { impl Action for GetTree {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Tree<SmallMessage>;
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let msgs = conn let msgs = conn
.prepare( .prepare(
" "
@ -716,15 +630,15 @@ impl Request for GetTree {
}) })
})? })?
.collect::<rusqlite::Result<_>>()?; .collect::<rusqlite::Result<_>>()?;
let tree = Tree::new(self.root_id, msgs); Ok(Tree::new(self.root_id, msgs))
let _ = self.result.send(tree);
Ok(())
} }
} }
impl Request for GetFirstRootId { impl Action for GetFirstRootId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let root_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -738,14 +652,15 @@ impl Request for GetFirstRootId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(root_id)
Ok(())
} }
} }
impl Request for GetLastRootId { impl Action for GetLastRootId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let root_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -759,14 +674,15 @@ impl Request for GetLastRootId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(root_id)
Ok(())
} }
} }
impl Request for GetPrevRootId { impl Action for GetPrevRootId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let root_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -781,14 +697,15 @@ impl Request for GetPrevRootId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(root_id)
Ok(())
} }
} }
impl Request for GetNextRootId { impl Action for GetNextRootId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let root_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -803,14 +720,15 @@ impl Request for GetNextRootId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(root_id)
Ok(())
} }
} }
impl Request for GetOldestMsgId { impl Action for GetOldestMsgId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let msg_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -824,14 +742,15 @@ impl Request for GetOldestMsgId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(msg_id)
Ok(())
} }
} }
impl Request for GetNewestMsgId { impl Action for GetNewestMsgId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let msg_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -845,14 +764,15 @@ impl Request for GetNewestMsgId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(msg_id)
Ok(())
} }
} }
impl Request for GetOlderMsgId { impl Action for GetOlderMsgId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let msg_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -867,13 +787,14 @@ impl Request for GetOlderMsgId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(msg_id)
Ok(())
} }
} }
impl Request for GetNewerMsgId { impl Action for GetNewerMsgId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let msg_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -888,14 +809,15 @@ impl Request for GetNewerMsgId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(msg_id)
Ok(())
} }
} }
impl Request for GetOldestUnseenMsgId { impl Action for GetOldestUnseenMsgId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let msg_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -910,14 +832,15 @@ impl Request for GetOldestUnseenMsgId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(msg_id)
Ok(())
} }
} }
impl Request for GetNewestUnseenMsgId { impl Action for GetNewestUnseenMsgId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let msg_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -932,14 +855,15 @@ impl Request for GetNewestUnseenMsgId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(msg_id)
Ok(())
} }
} }
impl Request for GetOlderUnseenMsgId { impl Action for GetOlderUnseenMsgId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let msg_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -955,14 +879,15 @@ impl Request for GetOlderUnseenMsgId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(msg_id)
Ok(())
} }
} }
impl Request for GetNewerUnseenMsgId { impl Action for GetNewerUnseenMsgId {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Option<MessageId>;
let tree = conn
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let msg_id = conn
.prepare( .prepare(
" "
SELECT id SELECT id
@ -978,13 +903,14 @@ impl Request for GetNewerUnseenMsgId {
row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0)) row.get::<_, WSnowflake>(0).map(|s| MessageId(s.0))
}) })
.optional()?; .optional()?;
let _ = self.result.send(tree); Ok(msg_id)
Ok(())
} }
} }
impl Request for GetUnseenMsgsCount { impl Action for GetUnseenMsgsCount {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = usize;
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let amount = conn let amount = conn
.prepare( .prepare(
" "
@ -996,13 +922,14 @@ impl Request for GetUnseenMsgsCount {
.query_row(params![self.room], |row| row.get(0)) .query_row(params![self.room], |row| row.get(0))
.optional()? .optional()?
.unwrap_or(0); .unwrap_or(0);
let _ = self.result.send(amount); Ok(amount)
Ok(())
} }
} }
impl Request for SetSeen { impl Action for SetSeen {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = ();
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
conn.execute( conn.execute(
" "
UPDATE euph_msgs UPDATE euph_msgs
@ -1016,8 +943,10 @@ impl Request for SetSeen {
} }
} }
impl Request for SetOlderSeen { impl Action for SetOlderSeen {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = ();
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
conn.execute( conn.execute(
" "
UPDATE euph_msgs UPDATE euph_msgs
@ -1032,8 +961,10 @@ impl Request for SetOlderSeen {
} }
} }
impl Request for GetChunkAtOffset { impl Action for GetChunkAtOffset {
fn perform(self, conn: &mut Connection) -> rusqlite::Result<()> { type Result = Vec<Message>;
fn run(self, conn: &mut Connection) -> rusqlite::Result<Self::Result> {
let mut query = conn.prepare( let mut query = conn.prepare(
" "
SELECT SELECT
@ -1073,7 +1004,83 @@ impl Request for GetChunkAtOffset {
}) })
})? })?
.collect::<rusqlite::Result<_>>()?; .collect::<rusqlite::Result<_>>()?;
let _ = self.result.send(messages); Ok(messages)
Ok(()) }
}
#[async_trait]
impl MsgStore<SmallMessage> for EuphRoomVault {
type Error = vault::tokio::Error;
async fn path(&self, id: &MessageId) -> Result<Path<MessageId>, Self::Error> {
self.path(*id).await
}
async fn msg(&self, id: &MessageId) -> Result<Option<SmallMessage>, Self::Error> {
self.msg(*id).await
}
async fn tree(&self, root_id: &MessageId) -> Result<Tree<SmallMessage>, Self::Error> {
self.tree(*root_id).await
}
async fn first_root_id(&self) -> Result<Option<MessageId>, Self::Error> {
self.first_root_id().await
}
async fn last_root_id(&self) -> Result<Option<MessageId>, Self::Error> {
self.last_root_id().await
}
async fn prev_root_id(&self, root_id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
self.prev_root_id(*root_id).await
}
async fn next_root_id(&self, root_id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
self.next_root_id(*root_id).await
}
async fn oldest_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
self.oldest_msg_id().await
}
async fn newest_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
self.newest_msg_id().await
}
async fn older_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
self.older_msg_id(*id).await
}
async fn newer_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
self.newer_msg_id(*id).await
}
async fn oldest_unseen_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
self.oldest_unseen_msg_id().await
}
async fn newest_unseen_msg_id(&self) -> Result<Option<MessageId>, Self::Error> {
self.newest_unseen_msg_id().await
}
async fn older_unseen_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
self.older_unseen_msg_id(*id).await
}
async fn newer_unseen_msg_id(&self, id: &MessageId) -> Result<Option<MessageId>, Self::Error> {
self.newer_unseen_msg_id(*id).await
}
async fn unseen_msgs_count(&self) -> Result<usize, Self::Error> {
self.unseen_msgs_count().await
}
async fn set_seen(&self, id: &MessageId, seen: bool) -> Result<(), Self::Error> {
self.set_seen(*id, seen).await
}
async fn set_older_seen(&self, id: &MessageId, seen: bool) -> Result<(), Self::Error> {
self.set_older_seen(*id, seen).await
} }
} }

View file

@ -1,25 +1,10 @@
use rusqlite::{Connection, Transaction}; use rusqlite::Transaction;
use vault::Migration;
pub fn migrate(conn: &mut Connection) -> rusqlite::Result<()> { pub const MIGRATIONS: [Migration; 2] = [m1, m2];
let mut tx = conn.transaction()?;
let user_version: usize = fn m1(tx: &mut Transaction<'_>, nr: usize, total: usize) -> rusqlite::Result<()> {
tx.query_row("SELECT * FROM pragma_user_version", [], |r| r.get(0))?; eprintln!("Migrating vault from {} to {} (out of {total})", nr, nr + 1);
let total = MIGRATIONS.len();
assert!(user_version <= total, "malformed database schema");
for (i, migration) in MIGRATIONS.iter().enumerate().skip(user_version) {
eprintln!("Migrating vault from {} to {} (out of {})", i, i + 1, total);
migration(&mut tx)?;
}
tx.pragma_update(None, "user_version", total)?;
tx.commit()
}
const MIGRATIONS: [fn(&mut Transaction<'_>) -> rusqlite::Result<()>; 2] = [m1, m2];
fn m1(tx: &mut Transaction<'_>) -> rusqlite::Result<()> {
tx.execute_batch( tx.execute_batch(
" "
CREATE TABLE euph_rooms ( CREATE TABLE euph_rooms (
@ -81,7 +66,8 @@ fn m1(tx: &mut Transaction<'_>) -> rusqlite::Result<()> {
) )
} }
fn m2(tx: &mut Transaction<'_>) -> rusqlite::Result<()> { fn m2(tx: &mut Transaction<'_>, nr: usize, total: usize) -> rusqlite::Result<()> {
eprintln!("Migrating vault from {} to {} (out of {total})", nr, nr + 1);
tx.execute_batch( tx.execute_batch(
" "
ALTER TABLE euph_msgs ALTER TABLE euph_msgs