aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLorenz Leitner <lrnz.ltnr@gmail.com>2021-10-10 15:12:41 +0200
committerLorenz Leitner <lrnz.ltnr@gmail.com>2021-10-12 12:06:57 +0200
commited1c228094188d872ceb8407fb6f46ff698937c2 (patch)
tree7d97b610220ff57cfcbf874b45936a88571de8a7 /src
parentBasic (re)implementation of gonzobot wolfram alpha (diff)
Add test
Put WA api response JSON into test resource file Add short url, increase concurrency Move shorten_url to util dir
Diffstat (limited to 'src')
-rw-r--r--src/hooks/wa.rs107
-rw-r--r--src/lib.rs1
-rw-r--r--src/util/mod.rs1
-rw-r--r--src/util/web.rs20
4 files changed, 97 insertions, 32 deletions
diff --git a/src/hooks/wa.rs b/src/hooks/wa.rs
index b6d4eff..7d087ea 100644
--- a/src/hooks/wa.rs
+++ b/src/hooks/wa.rs
@@ -1,11 +1,12 @@
-use anyhow::{bail, Context, Error, Result};
+use crate::util::web::shorten_url;
+use anyhow::{Context, Error, Result};
+use futures::try_join;
use reqwest::{get, Url};
use serde::{Deserialize, Serialize};
use serde_json::Result as SerdeJsonResult;
-use tracing::trace;
#[derive(Serialize, Deserialize, Debug)]
-struct WaResult {
+struct WaResponse {
queryresult: QueryResult,
}
@@ -18,41 +19,28 @@ struct QueryResult {
struct Pod {
title: String,
id: String,
+ primary: Option<bool>,
subpods: Vec<SubPod>,
}
#[derive(Serialize, Deserialize, Debug)]
struct SubPod {
- title: String,
plaintext: String,
}
-fn parse_json(str_data: &str) -> SerdeJsonResult<WaResult> {
- let w: WaResult = serde_json::from_str(str_data)?;
+fn parse_json(str_data: &str) -> SerdeJsonResult<WaResponse> {
+ let w: WaResponse = serde_json::from_str(str_data)?;
Ok(w)
}
-#[tracing::instrument]
-async fn wa_query(query_str: &str) -> Result<String, Error> {
- let app_id = "XXX"; // TODO: Get from env
- let api_url = format!(
- "http://api.wolframalpha.com/v2/query?input={}&appid={}&output=json",
- query_str, app_id
- );
-
- let body = get(Url::parse(&api_url).context("Failed to parse url")?)
- .await
- .context("Failed to make request")?
- .text()
- .await
- .context("failed to get request response text")?;
-
- let full_wa_res = parse_json(&body)?;
- trace!("got full_wa_res: {:?}", full_wa_res);
-
- let pod_plaintexts = full_wa_res.queryresult.pods
+/// Reduces all 'pod' plaintexts to a single string.
+/// Same as gonzobot does it.
+fn to_single_string(wa_res: WaResponse) -> String {
+ wa_res
+ .queryresult
+ .pods
.iter()
- .filter(|it| it.id.to_lowercase() != "input")
+ .filter(|it| it.id.to_lowercase() != "input" && it.primary.is_some())
.map(|pod| {
let subpod_texts = pod
.subpods
@@ -64,9 +52,55 @@ async fn wa_query(query_str: &str) -> Result<String, Error> {
format!("{}: {}", &pod.title, subpod_texts)
})
.collect::<Vec<String>>()
- .join(" - ");
+ .join(" - ")
+}
- Ok(pod_plaintexts)
+fn get_url(query_str: &str, base_url: Option<&str>) -> String {
+ let app_id = "XXX"; // TODO: Get from env
+ let wa_url = "http://api.wolframalpha.com";
+ let api_url = format!(
+ "{}/v2/query?input={}&appid={}&output=json",
+ base_url.unwrap_or(wa_url),
+ query_str,
+ app_id
+ );
+ api_url
+}
+
+async fn send_wa_req(url: &str) -> Result<String, Error> {
+ let body = get(Url::parse(url).context("Failed to parse url")?)
+ .await
+ .context("Failed to make request")?
+ .text()
+ .await
+ .context("failed to get request response text")?;
+ Ok(body)
+}
+
+async fn handle_wa_req(url: &str) -> Result<WaResponse, Error> {
+ let res_body = send_wa_req(url).await?;
+ let parsed = parse_json(&res_body)?;
+ Ok(parsed)
+}
+
+/// Sends a request to the Wolfram Alpha API, returns a plain text response.
+#[tracing::instrument]
+async fn wa_query(query_str: &str, base_url: Option<&str>) -> Result<String, Error> {
+ let user_url = format!("http://www.wolframalpha.com/input/?i={}", query_str);
+ let user_url_shortened_fut = shorten_url(&user_url);
+
+ let url = get_url(query_str, base_url);
+ let wa_res_fut = handle_wa_req(&url);
+
+ // Can't just (foo.await, bar.await), smh
+ // https://rust-lang.github.io/async-book/06_multiple_futures/02_join.html
+ let (wa_res, user_url_shortened) = try_join!(wa_res_fut, user_url_shortened_fut)?;
+
+ Ok(format!(
+ "{} - {}",
+ &user_url_shortened,
+ to_single_string(wa_res)
+ ))
}
#[cfg(test)]
@@ -74,11 +108,20 @@ mod tests {
use super::wa_query;
use anyhow::{Error, Result};
+ use mockito::{self, Matcher};
#[tokio::test]
- // async fn test_query_result_json_parsing() -> Result<(), Error> {
- async fn test_query_result_json_parsing() {
- let res = wa_query("weather graz").await.unwrap();
- assert_eq!(res, "asdf");
+ async fn test_query_result_json_parsing() -> Result<(), Error> {
+ let body = include_str!("../../tests/resources/wolfram_alpha_api_response.json");
+ let _m = mockito::mock("GET", Matcher::Any)
+ // Trimmed down version of a full WA response:
+ .with_body(body)
+ .create();
+ mockito::start();
+
+ let res = wa_query("5/10", Some(&mockito::server_url())).await?;
+ let res_without_link = res.splitn(2, "-").collect::<Vec<&str>>()[1].trim();
+ assert_eq!(res_without_link, "Exact result: 1/2 - Decimal form: 0.5");
+ Ok(())
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 220c3af..0aba61c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,6 +10,7 @@ use tracing::info;
pub mod config;
pub mod hooks;
+pub mod util;
pub use macros::catinator;
diff --git a/src/util/mod.rs b/src/util/mod.rs
new file mode 100644
index 0000000..fdd4603
--- /dev/null
+++ b/src/util/mod.rs
@@ -0,0 +1 @@
+pub mod web;
diff --git a/src/util/web.rs b/src/util/web.rs
new file mode 100644
index 0000000..77e20a3
--- /dev/null
+++ b/src/util/web.rs
@@ -0,0 +1,20 @@
+use anyhow::{Context, Error, Result};
+use reqwest::{get, Url};
+
+// TODO: Either catinator should have a URL shortening utility module,
+// or we should start our own service
+pub(crate) async fn shorten_url(url: &str) -> Result<String, Error> {
+ // This just uses the first service gonzobot uses too
+ let short_url = get(Url::parse(&format!(
+ "https://is.gd/create.php?format=simple&url={}",
+ url
+ ))
+ .context("Failed to parse url")?)
+ .await
+ .context("Failed to make request")?
+ .text()
+ .await
+ .context("failed to get request response text")?;
+
+ Ok(short_url)
+}