Improve the web service

This commit is contained in:
lemon-sh 2022-01-30 18:26:09 +01:00
parent 336ed231f7
commit d191a339f8
4 changed files with 64 additions and 44 deletions

View file

@ -1,4 +1,4 @@
use rusqlite::{params, OptionalExtension}; use rusqlite::{params, OptionalExtension, Params};
use serde::Serialize; use serde::Serialize;
use tokio::sync::{ use tokio::sync::{
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
@ -10,7 +10,7 @@ enum Task {
AddQuote(oneshot::Sender<bool>, Quote), AddQuote(oneshot::Sender<bool>, Quote),
GetQuote(oneshot::Sender<Option<Quote>>, Option<String>), GetQuote(oneshot::Sender<Option<Quote>>, Option<String>),
Search(oneshot::Sender<Option<Vec<Quote>>>, String), Search(oneshot::Sender<Option<Vec<Quote>>>, String),
Random20(oneshot::Sender<Option<Vec<Quote>>>) Random20(oneshot::Sender<Option<Vec<Quote>>>),
} }
pub struct DbExecutor { pub struct DbExecutor {
@ -63,35 +63,39 @@ impl DbExecutor {
tx.send(quote).unwrap(); tx.send(quote).unwrap();
} }
Task::Search(tx, query) => { Task::Search(tx, query) => {
tx.send(match self.db tx.send(self.yield_quotes("select quote,username from quotes where quote like '%'||?1||'%' order by quote asc limit 50", params![query])).unwrap();
.prepare("select quote,username from quotes where quote like '%'||?1||'%' order by quote asc limit 50")
.and_then(|mut v| v.query(params![query])
.and_then(|mut v| {
let mut quotes: Vec<Quote> = Vec::with_capacity(50);
while let Some(row) = v.next()? {
quotes.push(Quote {
quote: row.get(0)?,
author: row.get(1)?,
});
}
Ok(quotes)
}))
{
Ok(o) => {
Some(o)
}
Err(e) => {
tracing::error!("A database error has occurred: {}", e);
None
}
}).unwrap();
} }
Task::Random20(tx) => { Task::Random20(tx) => {
tx.send(None).unwrap(); tx.send(self.yield_quotes(
"select quote,username from quotes order by random() limit 20",
params![],
))
.unwrap();
} }
} }
} }
} }
fn yield_quotes<P: Params>(&self, sql: &str, params: P) -> Option<Vec<Quote>> {
match self.db.prepare(sql).and_then(|mut v| {
v.query(params).and_then(|mut v| {
let mut quotes: Vec<Quote> = Vec::with_capacity(50);
while let Some(row) = v.next()? {
quotes.push(Quote {
quote: row.get(0)?,
author: row.get(1)?,
});
}
Ok(quotes)
})
}) {
Ok(o) => Some(o),
Err(e) => {
tracing::error!("A database error has occurred: {}", e);
None
}
}
}
} }
pub struct ExecutorConnection { pub struct ExecutorConnection {
@ -107,13 +111,20 @@ impl Clone for ExecutorConnection {
} }
macro_rules! executor_wrapper { macro_rules! executor_wrapper {
($name:ident, $task:expr, $ret:ty, $($arg:ident: $ty:ty),*) => { ($name:ident, $task:expr, $ret:ty, $($arg:ident: $ty:ty),*) => {
pub async fn $name(&self, $($arg: $ty),*) -> $ret { pub async fn $name(&self, $($arg: $ty),*) -> $ret {
let (otx, orx) = oneshot::channel(); let (otx, orx) = oneshot::channel();
self.tx.send($task(otx, $($arg),*)).unwrap(); self.tx.send($task(otx, $($arg),*)).unwrap();
orx.await.unwrap() orx.await.unwrap()
} }
} };
($name:ident, $task:expr, $ret:ty) => {
pub async fn $name(&self) -> $ret {
let (otx, orx) = oneshot::channel();
self.tx.send($task(otx)).unwrap();
orx.await.unwrap()
}
};
} }
impl ExecutorConnection { impl ExecutorConnection {
@ -125,5 +136,11 @@ impl ExecutorConnection {
Option<Quote>, Option<Quote>,
author: Option<String> author: Option<String>
); );
executor_wrapper!(search, Task::Search, Option<Vec<Quote>>, query: String); executor_wrapper!(
search,
Task::Search,
Option<Vec<Quote>>,
query: String
);
executor_wrapper!(random20, Task::Random20, Option<Vec<Quote>>);
} }

View file

@ -296,7 +296,7 @@ async fn handle_privmsg(
if target == author { if target == author {
state state
.client .client
.send_privmsg(target, "You can't grab yourself")?; .send_privmsg(origin, "You can't grab yourself")?;
return Ok(()); return Ok(());
} }
if let Some(prev_msg) = state.last_msgs.get(target) { if let Some(prev_msg) = state.last_msgs.get(target) {
@ -308,16 +308,16 @@ async fn handle_privmsg(
}) })
.await .await
{ {
state.client.send_privmsg(target, "Quote added")?; state.client.send_privmsg(origin, "Quote added")?;
} else { } else {
state state
.client .client
.send_privmsg(target, "A database error has occurred")?; .send_privmsg(origin, "A database error has occurred")?;
} }
} else { } else {
state state
.client .client
.send_privmsg(target, "No previous messages to grab")?; .send_privmsg(origin, "No previous messages to grab")?;
} }
} else { } else {
state.client.send_privmsg(origin, "No nickname to grab")?; state.client.send_privmsg(origin, "No nickname to grab")?;

View file

@ -19,7 +19,7 @@
<form method="get"> <form method="get">
<label> <label>
Search query Search query
<input type="text" name="query" placeholder="Search..." required> <input type="text" name="q" placeholder="Search..." required>
</label> </label>
<button type="submit">Search</button> <button type="submit">Search</button>
</form> </form>

View file

@ -58,16 +58,19 @@ struct QuotesTemplate {
#[derive(Deserialize)] #[derive(Deserialize)]
struct QuotesQuery { struct QuotesQuery {
query: Option<String>, q: Option<String>
} }
async fn handle_get_quote(query: QuotesQuery, db: ExecutorConnection) -> impl Reply { async fn handle_get_quote(query: QuotesQuery, db: ExecutorConnection) -> impl Reply {
let template = if let Some(query) = query.query { let template = if let Some(q) = query.q {
if let Some(quotes) = db.search(query.clone()).await { if let Some(quotes) = db.search(q.clone()).await {
let quotes_count = quotes.len(); let quotes_count = quotes.len();
QuotesTemplate { QuotesTemplate {
quotes: Some(quotes), quotes: Some(quotes),
flash: Some(format!("Displaying {}/50 results for query \"{}\"", quotes_count, query)), flash: Some(format!(
"Displaying {}/50 results for query \"{}\"",
quotes_count, q
)),
} }
} else { } else {
QuotesTemplate { QuotesTemplate {
@ -77,8 +80,8 @@ async fn handle_get_quote(query: QuotesQuery, db: ExecutorConnection) -> impl Re
} }
} else { } else {
QuotesTemplate { QuotesTemplate {
quotes: None, quotes: db.random20().await,
flash: None, flash: Some("Displaying up to 20 random quotes".into()),
} }
}; };
match HANDLEBARS.render("quotes", &template) { match HANDLEBARS.render("quotes", &template) {