use async_trait::async_trait; use chrono::{DateTime, Utc}; use rusqlite::{params, Connection, OptionalExtension}; use tokio::sync::{mpsc, oneshot}; use crate::euph::api::Snowflake; use crate::store::{Msg, MsgStore, Path, Tree}; use super::Request; #[derive(Debug, Clone)] pub struct EuphMsg { id: Snowflake, parent: Option, time: DateTime, nick: String, content: String, } impl Msg for EuphMsg { type Id = Snowflake; fn id(&self) -> Self::Id { self.id } fn parent(&self) -> Option { self.parent } fn time(&self) -> DateTime { self.time } fn nick(&self) -> String { self.nick.clone() } fn content(&self) -> String { self.content.clone() } } impl From for Request { fn from(r: EuphRequest) -> Self { Self::Euph(r) } } pub struct EuphVault { pub(super) tx: mpsc::Sender, pub(super) room: String, } #[async_trait] impl MsgStore for EuphVault { async fn path(&self, id: &Snowflake) -> Path { // TODO vault::Error let (tx, rx) = oneshot::channel(); let request = EuphRequest::Path { room: self.room.clone(), id: *id, result: tx, }; let _ = self.tx.send(request.into()).await; rx.await.unwrap() } async fn tree(&self, root: &Snowflake) -> Tree { // TODO vault::Error let (tx, rx) = oneshot::channel(); let request = EuphRequest::Tree { room: self.room.clone(), root: *root, result: tx, }; let _ = self.tx.send(request.into()).await; rx.await.unwrap() } async fn prev_tree(&self, root: &Snowflake) -> Option { // TODO vault::Error let (tx, rx) = oneshot::channel(); let request = EuphRequest::PrevTree { room: self.room.clone(), root: *root, result: tx, }; let _ = self.tx.send(request.into()).await; rx.await.unwrap() } async fn next_tree(&self, root: &Snowflake) -> Option { // TODO vault::Error let (tx, rx) = oneshot::channel(); let request = EuphRequest::NextTree { room: self.room.clone(), root: *root, result: tx, }; let _ = self.tx.send(request.into()).await; rx.await.unwrap() } async fn first_tree(&self) -> Option { // TODO vault::Error let (tx, rx) = oneshot::channel(); let request = EuphRequest::FirstTree { room: self.room.clone(), result: tx, }; let _ = self.tx.send(request.into()).await; rx.await.unwrap() } async fn last_tree(&self) -> Option { // TODO vault::Error let (tx, rx) = oneshot::channel(); let request = EuphRequest::LastTree { room: self.room.clone(), result: tx, }; let _ = self.tx.send(request.into()).await; rx.await.unwrap() } } pub(super) enum EuphRequest { Path { room: String, id: Snowflake, result: oneshot::Sender>, }, Tree { room: String, root: Snowflake, result: oneshot::Sender>, }, PrevTree { room: String, root: Snowflake, result: oneshot::Sender>, }, NextTree { room: String, root: Snowflake, result: oneshot::Sender>, }, FirstTree { room: String, result: oneshot::Sender>, }, LastTree { room: String, result: oneshot::Sender>, }, } impl EuphRequest { pub(super) fn perform(self, conn: &Connection) { let _ = match self { EuphRequest::Path { room, id, result } => Self::path(conn, room, id, result), EuphRequest::Tree { room, root, result } => Self::tree(conn, room, root, result), EuphRequest::PrevTree { room, root, result } => { Self::prev_tree(conn, room, root, result) } EuphRequest::NextTree { room, root, result } => { Self::next_tree(conn, room, root, result) } EuphRequest::FirstTree { room, result } => Self::first_tree(conn, room, result), EuphRequest::LastTree { room, result } => Self::last_tree(conn, room, result), }; } fn path( conn: &Connection, room: String, id: Snowflake, result: oneshot::Sender>, ) -> rusqlite::Result<()> { let path = conn .prepare( " WITH RECURSIVE path (room, id) = ( VALUES (?, ?) UNION SELECT (room, parent) FROM euph_msgs JOIN path USING (room, id) ) SELECT id FROM path ORDER BY id ASC ", )? .query_map(params![room, id.0], |row| row.get(0).map(Snowflake))? .collect::>()?; let path = Path::new(path); let _ = result.send(path); Ok(()) } fn tree( conn: &Connection, room: String, root: Snowflake, result: oneshot::Sender>, ) -> rusqlite::Result<()> { let msgs = conn .prepare( " WITH RECURSIVE tree (room, id) = ( VALUES (?, ?) UNION SELECT (euph_msgs.room, euph_msgs.id) FROM euph_msgs JOIN tree ON tree.room = euph_msgs.room AND tree.id = euph_msgs.parent ) SELECT (id, parent, time, name, content) FROM euph_msg JOIN tree USING (room, id) ORDER BY id ASC ", )? .query_map(params![room, root.0], |row| { Ok(EuphMsg { id: Snowflake(row.get(0)?), parent: row.get::<_, Option>(1)?.map(Snowflake), time: row.get(2)?, nick: row.get(3)?, content: row.get(4)?, }) })? .collect::>()?; let tree = Tree::new(root, msgs); let _ = result.send(tree); Ok(()) } fn prev_tree( conn: &Connection, room: String, root: Snowflake, result: oneshot::Sender>, ) -> rusqlite::Result<()> { let tree = conn .prepare( " SELECT id FROM euph_trees WHERE room = ? AND id < ? ORDER BY id DESC LIMIT 1 ", )? .query_row(params![room, root.0], |row| row.get(0).map(Snowflake)) .optional()?; let _ = result.send(tree); Ok(()) } fn next_tree( conn: &Connection, room: String, root: Snowflake, result: oneshot::Sender>, ) -> rusqlite::Result<()> { let tree = conn .prepare( " SELECT id FROM euph_trees WHERE room = ? AND id > ? ORDER BY id ASC LIMIT 1 ", )? .query_row(params![room, root.0], |row| row.get(0).map(Snowflake)) .optional()?; let _ = result.send(tree); Ok(()) } fn first_tree( conn: &Connection, room: String, result: oneshot::Sender>, ) -> rusqlite::Result<()> { let tree = conn .prepare( " SELECT id FROM euph_trees WHERE room = ? ORDER BY id ASC LIMIT 1 ", )? .query_row([room], |row| row.get(0).map(Snowflake)) .optional()?; let _ = result.send(tree); Ok(()) } fn last_tree( conn: &Connection, room: String, result: oneshot::Sender>, ) -> rusqlite::Result<()> { let tree = conn .prepare( " SELECT id FROM euph_trees WHERE room = ? ORDER BY id DESC LIMIT 1 ", )? .query_row([room], |row| row.get(0).map(Snowflake)) .optional()?; let _ = result.send(tree); Ok(()) } }