use crate::common::Lang;
pub trait API {
const BASE: &'static str;
const DATATYPE: &'static str;
#[must_use]
fn url(lang: Lang) -> String {
format!(
"https://data.weather.gov.hk/weatherAPI/opendata/{}.php?dataType={}&lang={lang}",
Self::BASE,
Self::DATATYPE,
)
}
}
macro_rules! impl_api {
($t:ty, $b:ident, $d:ident) => {
impl crate::fetch::API for $t {
const BASE: &'static str = stringify!($b);
const DATATYPE: &'static str = stringify!($d);
}
};
}
#[allow(unused_imports)]
pub(crate) use impl_api;
#[cfg(feature = "fetch")]
#[cfg_attr(docsrs, doc(cfg(feature = "fetch")))]
pub trait Fetch: Sized {
#[allow(clippy::missing_errors_doc)]
fn fetch(lang: Lang) -> impl std::future::Future<Output = anyhow::Result<Self>> + Send;
#[allow(clippy::missing_errors_doc)]
fn fetch_with_client(
lang: Lang,
client: reqwest::Client,
) -> impl std::future::Future<Output = anyhow::Result<Self>> + Send;
}
#[cfg(feature = "fetch")]
#[cfg_attr(docsrs, doc(cfg(feature = "fetch")))]
impl<T> Fetch for T
where
T: API + serde::de::DeserializeOwned,
{
async fn fetch(lang: Lang) -> anyhow::Result<Self> {
let client = reqwest::Client::builder().build()?;
Self::fetch_with_client(lang, client).await
}
async fn fetch_with_client(lang: Lang, client: reqwest::Client) -> anyhow::Result<Self> {
Ok(client.get(Self::url(lang)).send().await?.json().await?)
}
}
#[allow(clippy::missing_errors_doc)]
#[cfg(feature = "fetch")]
#[cfg_attr(docsrs, doc(cfg(feature = "fetch")))]
pub async fn fetch<T>(lang: Lang) -> anyhow::Result<T>
where
T: Fetch,
{
T::fetch(lang).await
}
#[allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
#[cfg(feature = "fetch")]
#[cfg_attr(docsrs, doc(cfg(feature = "fetch")))]
pub async fn fetch_with_client<T>(lang: Lang, client: reqwest::Client) -> anyhow::Result<T>
where
T: Fetch,
{
T::fetch_with_client(lang, client).await
}