2020-12-20 20:52:22 +01:00
|
|
|
use crate::api::Meme;
|
|
|
|
use anyhow::bail;
|
2020-12-20 23:30:58 +01:00
|
|
|
use reqwest::Client;
|
2020-12-20 20:52:22 +01:00
|
|
|
use std::str::FromStr;
|
2020-12-05 22:20:08 +01:00
|
|
|
use term_table::{Table, TableBuilder, TableStyle};
|
2020-12-05 12:08:24 +01:00
|
|
|
use tokio::process::Command;
|
|
|
|
|
2020-12-19 19:43:23 +01:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! init_once_cell {
|
|
|
|
($cell:ident, $init_fn:expr) => {
|
|
|
|
match $cell.get() {
|
|
|
|
Some(x) => x,
|
|
|
|
None => {
|
|
|
|
let x = $init_fn;
|
|
|
|
$cell.get_or_init(|| x)
|
2020-12-20 20:52:22 +01:00
|
|
|
},
|
2020-12-19 19:43:23 +01:00
|
|
|
}
|
2020-12-20 20:52:22 +01:00
|
|
|
};
|
2020-12-05 12:08:24 +01:00
|
|
|
}
|
2020-12-05 12:32:01 +01:00
|
|
|
|
2020-12-20 23:30:58 +01:00
|
|
|
pub mod consts {
|
|
|
|
pub const NO_SUCH_CATEGORY_ERROR: &str = "The given Category does not exist!";
|
|
|
|
pub const NO_SUCH_USER_ERROR: &str = "The given User does not exist!";
|
|
|
|
}
|
|
|
|
|
2020-12-19 19:43:23 +01:00
|
|
|
/// ways to communicyte with the JM API
|
|
|
|
pub mod api {
|
2020-12-20 20:52:22 +01:00
|
|
|
use crate::api::{CatsResp, Meme, MemesResp, User, UsersResp};
|
|
|
|
use anyhow::Result;
|
2020-12-19 19:43:23 +01:00
|
|
|
use log::info;
|
2020-12-20 20:52:22 +01:00
|
|
|
use once_cell::sync::OnceCell;
|
2020-12-19 19:43:23 +01:00
|
|
|
use reqwest::Client;
|
2020-12-29 14:19:51 +01:00
|
|
|
use url::Url;
|
2020-12-19 19:43:23 +01:00
|
|
|
|
|
|
|
// cached api responses
|
|
|
|
static CATS: OnceCell<Vec<String>> = OnceCell::new();
|
|
|
|
static MEMES: OnceCell<Vec<Meme>> = OnceCell::new();
|
|
|
|
static USERS: OnceCell<Vec<User>> = OnceCell::new();
|
|
|
|
|
|
|
|
pub async fn cats(http: &Client) -> Result<&Vec<String>> {
|
|
|
|
Ok(init_once_cell!(CATS, {
|
2020-12-18 16:58:53 +01:00
|
|
|
info!("Requesting categories from server");
|
|
|
|
let res = http
|
|
|
|
.get("https://data.tilera.xyz/api/jensmemes/categories")
|
|
|
|
.send()
|
|
|
|
.await?;
|
|
|
|
let cats = serde_json::from_slice::<CatsResp>(&res.bytes().await?)?;
|
2020-12-19 19:43:23 +01:00
|
|
|
cats.categories.into_iter().map(|c| c.id).collect()
|
|
|
|
}))
|
|
|
|
}
|
2020-12-05 12:32:01 +01:00
|
|
|
|
2020-12-29 14:19:51 +01:00
|
|
|
pub async fn memes<'a>(
|
|
|
|
http: &Client,
|
|
|
|
cat_filter: Option<&str>,
|
|
|
|
usr_filter: Option<&str>,
|
|
|
|
) -> Result<&'a Vec<Meme>> {
|
2020-12-19 19:43:23 +01:00
|
|
|
Ok(init_once_cell!(MEMES, {
|
2020-12-29 14:19:51 +01:00
|
|
|
let mut url = Url::options().parse("https://data.tilera.xyz/api/jensmemes/memes")?;
|
|
|
|
let mut pairs = url.query_pairs_mut();
|
|
|
|
|
|
|
|
if let Some(cat) = cat_filter {
|
|
|
|
pairs.append_pair("category", cat);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(usr) = usr_filter {
|
|
|
|
pairs.append_pair("user", usr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// drop required in order to move the URL into the request
|
|
|
|
drop(pairs);
|
|
|
|
|
2020-12-18 16:58:53 +01:00
|
|
|
info!("Requesting memes from server");
|
2020-12-29 14:19:51 +01:00
|
|
|
let res = http.get(url).send().await?;
|
2020-12-18 16:58:53 +01:00
|
|
|
let memes = serde_json::from_slice::<MemesResp>(&res.bytes().await?)?;
|
2020-12-20 20:52:22 +01:00
|
|
|
|
2020-12-19 19:43:23 +01:00
|
|
|
memes.memes
|
|
|
|
}))
|
|
|
|
}
|
2020-12-05 22:20:08 +01:00
|
|
|
|
2020-12-19 19:43:23 +01:00
|
|
|
pub async fn users(http: &Client) -> Result<&Vec<User>> {
|
|
|
|
Ok(init_once_cell!(USERS, {
|
|
|
|
info!("Requesting users from server");
|
2020-12-20 20:52:22 +01:00
|
|
|
let res = http
|
|
|
|
.get("https://data.tilera.xyz/api/jensmemes/users")
|
|
|
|
.send()
|
|
|
|
.await?;
|
2020-12-19 19:43:23 +01:00
|
|
|
let users = serde_json::from_slice::<UsersResp>(&res.bytes().await?)?;
|
|
|
|
users.users
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn open_link(url: &str) -> anyhow::Result<()> {
|
|
|
|
match std::env::var_os("BROWSER") {
|
|
|
|
Some(browser) => {
|
|
|
|
Command::new(&browser).arg(url).status().await?;
|
2020-12-18 16:58:53 +01:00
|
|
|
},
|
2020-12-19 19:43:23 +01:00
|
|
|
None => opener::open(&url)?,
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2020-12-05 22:20:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// returns an empty table with the correct format settings for lists
|
|
|
|
pub fn list_table<'a>() -> Table<'a> {
|
|
|
|
TableBuilder::new()
|
|
|
|
.style(TableStyle::simple())
|
|
|
|
.separate_rows(false)
|
|
|
|
.has_top_boarder(false)
|
|
|
|
.has_bottom_boarder(false)
|
|
|
|
.build()
|
|
|
|
}
|
2020-12-20 20:52:22 +01:00
|
|
|
|
|
|
|
pub enum MemeSorting {
|
|
|
|
Id,
|
|
|
|
Link,
|
|
|
|
Category,
|
|
|
|
User,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MemeSorting {
|
|
|
|
pub fn sort_with(&self, memes: &mut [&Meme]) {
|
|
|
|
macro_rules! sort {
|
|
|
|
($list:ident, $field:ident) => {
|
2020-12-20 23:30:58 +01:00
|
|
|
$list.sort_by(|a, b| {
|
|
|
|
a.$field
|
|
|
|
.to_ascii_lowercase()
|
|
|
|
.cmp(&b.$field.to_ascii_lowercase())
|
|
|
|
});
|
2020-12-20 20:52:22 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
match self {
|
|
|
|
Self::Id => sort!(memes, id),
|
|
|
|
Self::Link => sort!(memes, link),
|
|
|
|
Self::Category => sort!(memes, category),
|
|
|
|
Self::User => sort!(memes, user),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for MemeSorting {
|
|
|
|
type Err = anyhow::Error;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
match s.to_lowercase().as_ref() {
|
|
|
|
"id" => Ok(Self::Id),
|
|
|
|
"link" => Ok(Self::Link),
|
|
|
|
"category" => Ok(Self::Category),
|
|
|
|
"user" => Ok(Self::User),
|
|
|
|
_ => bail!("Invalid Meme sorting! options are id, link, category and user!"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-20 23:30:58 +01:00
|
|
|
pub async fn assert_user_exists(http: &Client, user: &str) -> anyhow::Result<()> {
|
|
|
|
if !api::users(http).await?.iter().any(|u| u.name == user) {
|
|
|
|
bail!(consts::NO_SUCH_USER_ERROR);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
pub async fn assert_category_exists(http: &Client, cat: &str) -> anyhow::Result<()> {
|
|
|
|
if !api::cats(http).await?.iter().any(|c| c == cat) {
|
|
|
|
bail!(consts::NO_SUCH_CATEGORY_ERROR);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|