use std::borrow::Cow; use reqwest::{Method, Response}; use secrecy::{ExposeSecret, SecretString}; use url::UrlQuery; macro_rules! const_from_env { ($const:ident, $env:literal) => { const $const: Option<&'static str> = option_env!($env); }; ($const:ident, $env:literal, $default:expr) => { const $const: &'static str = match option_env!($env) { Some(s) => s, None => $default, }; }; } const_from_env!(USER_AGENT_APP, "FROXY_UA_APP_NAME", "Froxy"); const_from_env!(USER_AGENT_VERSION, "FROXY_UA_APP_VERSION", { const_from_env!(CARGO_PKG_VERSION, "CARGO_PKG_VERSION", "0.1-alpha"); CARGO_PKG_VERSION }); const_from_env!( USER_AGENT_DESC, "FROXY_UA_DESC", "(by maiboyer; for maiboyer)" ); pub const DEFAULT_USER_AGENT: &str = constcat::concat!(USER_AGENT_APP, "/", USER_AGENT_VERSION, USER_AGENT_DESC); #[derive(Debug, Clone)] pub struct State { client: reqwest::Client, base_url: url::Url, secret_cookie: secrecy::SecretString, user_agent: std::borrow::Cow<'static, str>, } impl State { pub fn new(secret_cookie: impl AsRef) -> Self { let user_agent = DEFAULT_USER_AGENT; let base_url = "https://friends.42paris.fr"; Self::with_info( secret_cookie, user_agent.into(), url::Url::parse(base_url).expect("default base url not valid"), ) } pub fn with_info( secret_cookie: impl AsRef, user_agent: std::borrow::Cow<'static, str>, base_url: url::Url, ) -> Self { let client = reqwest::Client::builder() .user_agent(&*user_agent) .build() .unwrap(); Self { client, base_url, secret_cookie: secret_cookie.as_ref().into(), user_agent, } } pub fn client(&self) -> &reqwest::Client { &self.client } pub fn base_url(&self) -> &url::Url { &self.base_url } pub fn cookie(&self) -> &SecretString { &self.secret_cookie } pub fn user_agent(&self) -> &str { &self.user_agent } } impl State { pub async fn request( &self, method: Method, url: impl AsRef, qs: Option<&str>, body: Option>, ) -> Result { let mut u = self.base_url.clone(); u.set_path(url.as_ref()); u.set_query(qs); let builder = self .client .request(method, u) .header("Cookie", self.secret_cookie.expose_secret()); let builder = if let Some(body) = body { builder.body(body) } else { builder }; builder.send().await } pub async fn get( &self, url: impl AsRef, qs: Option<&str>, ) -> Result { self.request(Method::GET, url.as_ref(), qs, None).await } pub async fn post( &self, url: impl AsRef, qs: Option<&str>, ) -> Result { self.request(Method::POST, url.as_ref(), qs, None).await } pub async fn post_with_body( &self, url: impl AsRef, qs: Option<&str>, body: Vec, ) -> Result { self.request(Method::POST, url.as_ref(), qs, Some(body)) .await } }