jensmemesclient/cli/src/util.rs

129 lines
3.6 KiB
Rust

use crate::api::Meme;
use anyhow::bail;
use std::str::FromStr;
use term_table::{Table, TableBuilder, TableStyle};
use tokio::process::Command;
#[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)
},
}
};
}
/// ways to communicyte with the JM API
pub mod api {
use crate::api::{CatsResp, Meme, MemesResp, User, UsersResp};
use anyhow::Result;
use log::info;
use once_cell::sync::OnceCell;
use reqwest::Client;
// 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, {
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?)?;
cats.categories.into_iter().map(|c| c.id).collect()
}))
}
pub async fn memes(http: &Client) -> Result<&Vec<Meme>> {
Ok(init_once_cell!(MEMES, {
info!("Requesting memes from server");
let res = http
.get("https://data.tilera.xyz/api/jensmemes/memes")
.send()
.await?;
let memes = serde_json::from_slice::<MemesResp>(&res.bytes().await?)?;
memes.memes
}))
}
pub async fn users(http: &Client) -> Result<&Vec<User>> {
Ok(init_once_cell!(USERS, {
info!("Requesting users from server");
let res = http
.get("https://data.tilera.xyz/api/jensmemes/users")
.send()
.await?;
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?;
},
None => opener::open(&url)?,
}
Ok(())
}
/// 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()
}
pub enum MemeSorting {
Id,
Link,
Category,
User,
}
impl MemeSorting {
pub fn sort_with(&self, memes: &mut [&Meme]) {
macro_rules! sort {
($list:ident, $field:ident) => {
$list.sort_by(|a, b| a.$field.to_ascii_lowercase().cmp(&b.$field.to_ascii_lowercase()));
};
}
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!"),
}
}
}