Improve the web service
This commit is contained in:
parent
336ed231f7
commit
d191a339f8
|
@ -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>>);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")?;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue