#[allow(non_upper_case_globals)] use crate::{ MASTER_SERVER, MASTER_PORT, models::*, UResult, UError, utils::opt_to_string }; use reqwest::{ Client, Url, 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: Option<&$url_param>)? | { let request = ClientHandler::build_post( instance, &format!("{}/{}", stringify!($path), String::new() $(+ &opt_to_string(url as Option<&$url_param>) )? ) ); 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: Option<&$url_param>)? | { let request = ClientHandler::build_get( instance, &format!("{}/{}", stringify!($path), String::new() $(+ &opt_to_string(url as Option<&$url_param>) )? ) ); 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: Option<&$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 Option<&$url_param>)? ); let response = request.send().await?; let content_len = response.content_length(); let is_success = match response.error_for_status_ref() { Ok(_) => Ok(()), Err(e) => Err(UError::from(e)) }; match is_success { Ok(_) => response.json::>() .await .map(|msg| msg.into_inner()) .or_else(|e| { match content_len { Some(0) => Ok(Default::default()), _ => Err(UError::from(e)) } }), Err(UError::NetError(err_src, _)) => Err( UError::NetError( err_src, response.text().await.unwrap() ) ), _ => unreachable!() } } } 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/Uuid() -> Vec); // get jobs for client 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) -> ()); // create and upload job (A) build_handler!(POST upload_jobs(Vec) -> ()); // delete something (A) build_handler!(GET del/Uuid() -> String); // set jobs for client (A) // POST /set_jobs/Uuid json: Vec build_handler!(POST set_jobs/Uuid(Vec) -> ()); // report job result build_handler!(POST report(Vec) -> ());