#[allow(non_upper_case_globals)] use crate::{ MASTER_SERVER, MASTER_PORT, models::*, UResult, UError }; use reqwest::{ Client, Url, Response, RequestBuilder }; use std::{ net::Ipv4Addr, str::FromStr }; use uuid::Uuid; pub struct Paths; #[macro_export] macro_rules! build_url_by_method { ( POST $path:tt, pname = $($param_name:literal)?, ptype = $($param_type:ty)?, urlparam = $($url_param:ty)? ) => { | instance: &ClientHandler $(, param: &$param_type)? $(, url: &$url_param)? | { let request = ClientHandler::build_post( instance, &format!("{}/{}", stringify!($path), String::new() $(+ &(url as &$url_param).to_string())? ) ); request $( .json::>(¶m.as_message()) )? } }; ( GET $path:tt, pname = $($param_name:literal)?, ptype = $($param_type:ty)?, urlparam = $($url_param:ty)? ) => { | instance: &ClientHandler $(, param: &$param_type)? $(, url: &$url_param)? | { let request = ClientHandler::build_get( instance, &format!("{}/{}", stringify!($path), String::new() $(+ &(url as &$url_param).to_string())? ) ); request $( .query(&[(stringify!($param_name), param.to_string())]) )? } }; } // param_type and result must impl ToMsg #[macro_export] macro_rules! build_handler { ( $method:tt $path:tt $( /$url_param:tt )? ( $( $param_name:literal: )? $( $param_type:ty )? ) -> $result:ty ) => { impl ClientHandler { pub async fn $path( &self $(, param: &$param_type)? $(, url_param: &$url_param)? ) -> UResult<$result> { let request = $crate::build_url_by_method!( $method $path, pname = $($param_name)?, ptype = $($param_type)?, urlparam = $($url_param)? )(self $(, param as &$param_type)? $(, url_param as &$url_param)?); let response = request.send().await?; match response.error_for_status() { Ok(r) => r.json::>() .await .map_err(|e| UError::from(e)) .map(|msg| msg.into_item()), Err(e) => Err(UError::from(e)) } } } impl Paths { pub const $path: &'static str = stringify!($path); } }; } pub struct ClientHandler { base_url: Url, client: Client, password: Option } impl ClientHandler { pub fn new(server: Option) -> Self { let master_server = server .map(|s| Ipv4Addr::from_str(&s).unwrap()) .unwrap_or(MASTER_SERVER); Self { client: Client::new(), base_url: Url::parse( &format!("http://{}:{}", master_server, MASTER_PORT) ).unwrap(), password: None } } pub fn password(mut self, password: String) -> ClientHandler { self.password = Some(password); self } fn set_pwd(&self, rb: RequestBuilder) -> RequestBuilder { match &self.password { Some(p) => rb.bearer_auth(p), None => rb } } fn build_get(&self, url: &str) -> RequestBuilder { let rb = self.client.get(self.base_url.join(url).unwrap()); self.set_pwd(rb) } fn build_post(&self, url: &str) -> RequestBuilder { let rb = self.client.post(self.base_url.join(url).unwrap()); self.set_pwd(rb) } } ////////////////// // method basic_path(json/query param; additional_url_param) -> return value // A - admin only ////////////////// // client listing (A) build_handler!(GET get_agents() -> Vec); // get jobs for client (agent_id=Uuid) build_handler!(GET get_agent_jobs/Uuid() -> Vec); // get all available jobs (A) build_handler!(GET get_jobs/Uuid() -> Vec); // add client to server's db build_handler!(POST init(IAgent) -> String); // create and upload job (A) build_handler!(POST upload_jobs(Vec) -> ()); // delete something (A) build_handler!(GET del/Uuid() -> ()); // set jobs for client (A) // POST /set_jobs/Uuid json: Vec build_handler!(POST set_jobs/Uuid(Vec) -> ()); // get results (A) // GET /get_job_results/Uuid build_handler!(GET get_job_results/Uuid() -> Vec); // report job result build_handler!(POST report(Vec) -> ());