2022-01-02 14:58:54 -06:00
use rusqlite ::{ params , OptionalExtension } ;
use tokio ::sync ::{
mpsc ::{ unbounded_channel , UnboundedReceiver , UnboundedSender } ,
oneshot ,
} ;
2022-01-26 17:58:00 -06:00
use serde ::Serialize ;
2022-01-01 15:35:27 -06:00
#[ derive(Debug) ]
enum Task {
2022-01-26 17:58:00 -06:00
AddQuote ( oneshot ::Sender < bool > , Quote ) ,
GetQuote ( oneshot ::Sender < Option < Quote > > , Option < String > ) ,
2022-01-01 15:35:27 -06:00
// implement search WITH PAGINATION
}
pub struct DbExecutor {
rx : UnboundedReceiver < Task > ,
db : rusqlite ::Connection ,
}
2022-01-26 17:58:00 -06:00
#[ derive(Serialize, Debug) ]
pub struct Quote {
pub author : String ,
pub quote : String
}
2022-01-01 15:35:27 -06:00
impl DbExecutor {
pub fn create ( dbpath : & str ) -> rusqlite ::Result < ( Self , ExecutorConnection ) > {
let ( tx , rx ) = unbounded_channel ( ) ;
let db = rusqlite ::Connection ::open ( dbpath ) ? ;
2022-01-02 14:58:54 -06:00
db . execute (
" create table if not exists quotes(id integer primary key, \
username text not null , quote text not null ) " ,
[ ] ,
) ? ;
2022-01-01 15:35:27 -06:00
tracing ::debug! ( " Database connected ({}) " , dbpath ) ;
Ok ( ( Self { rx , db } , ExecutorConnection { tx } ) )
}
pub fn run ( mut self ) {
while let Some ( task ) = self . rx . blocking_recv ( ) {
match task {
2022-01-26 17:58:00 -06:00
Task ::AddQuote ( tx , quote ) = > {
2022-01-01 15:35:27 -06:00
if let Err ( e ) = self . db . execute (
2022-01-02 14:58:54 -06:00
" insert into quotes(quote,username) values(?,?) " ,
2022-01-26 17:58:00 -06:00
params! [ quote . quote , quote . author ] ,
2022-01-02 14:58:54 -06:00
) {
2022-01-01 15:35:27 -06:00
tracing ::error! ( " A database error has occurred: {} " , e ) ;
tx . send ( false ) . unwrap ( ) ;
} else {
tx . send ( true ) . unwrap ( ) ;
}
}
Task ::GetQuote ( tx , author ) = > {
let quote = if let Some ( ref author ) = author {
2022-01-26 17:58:00 -06:00
self . db . query_row ( " select quote,username from quotes where username=? order by random() limit 1 " , params! [ author ] , | v | Ok ( Quote { quote :v . get ( 0 ) ? , author :v . get ( 1 ) ? } ) )
2022-01-01 15:35:27 -06:00
} else {
2022-01-26 17:58:00 -06:00
self . db . query_row ( " select quote,username from quotes order by random() limit 1 " , params! [ ] , | v | Ok ( Quote { quote :v . get ( 0 ) ? , author :v . get ( 1 ) ? } ) )
2022-01-01 15:35:27 -06:00
} . optional ( ) . unwrap_or_else ( | e | {
tracing ::error! ( " A database error has occurred: {} " , e ) ;
None
} ) ;
tx . send ( quote ) . unwrap ( ) ;
}
}
}
}
}
pub struct ExecutorConnection {
tx : UnboundedSender < Task > ,
}
impl Clone for ExecutorConnection {
fn clone ( & self ) -> Self {
2022-01-02 14:58:54 -06:00
Self {
tx : self . tx . clone ( ) ,
}
2022-01-01 15:35:27 -06:00
}
}
impl ExecutorConnection {
2022-01-26 17:58:00 -06:00
pub async fn add_quote ( & self , quote : Quote ) -> bool {
2022-01-01 15:35:27 -06:00
let ( otx , orx ) = oneshot ::channel ( ) ;
2022-01-26 17:58:00 -06:00
self . tx . send ( Task ::AddQuote ( otx , quote ) ) . unwrap ( ) ;
2022-01-01 15:35:27 -06:00
orx . await . unwrap ( )
}
2022-01-26 17:58:00 -06:00
pub async fn get_quote ( & self , author : Option < String > ) -> Option < Quote > {
2022-01-01 15:35:27 -06:00
let ( otx , orx ) = oneshot ::channel ( ) ;
self . tx . send ( Task ::GetQuote ( otx , author ) ) . unwrap ( ) ;
orx . await . unwrap ( )
}
}