diff --git a/bin/u_server/src/main.rs b/bin/u_server/src/main.rs index 00ed177..a14710b 100644 --- a/bin/u_server/src/main.rs +++ b/bin/u_server/src/main.rs @@ -37,7 +37,6 @@ async fn main() { .and_then(handlers::listing); let auth_token = warp::header::exact("authorization", "Bearer 123qwe"); - //.and_then(handlers::check_token); let auth_zone = auth_token.and(ls); diff --git a/build_static_docker.sh b/build_static_docker.sh deleted file mode 100755 index 872696e..0000000 --- a/build_static_docker.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -docker run -v $PWD:/volume -w /volume -it clux/muslrust cargo build diff --git a/cargo_static_docker.sh b/cargo_static_docker.sh new file mode 100755 index 0000000..f0171fa --- /dev/null +++ b/cargo_static_docker.sh @@ -0,0 +1,8 @@ +#!/bin/bash +docker run \ + -v $PWD:/volume \ + -v cargo-cache:/root/.cargo/registry \ + -w /volume \ + -it \ + clux/muslrust \ + cargo $@ diff --git a/lib/u_lib/src/client/client.rs b/lib/u_lib/src/client/client.rs index b9f2d4e..2c568fc 100644 --- a/lib/u_lib/src/client/client.rs +++ b/lib/u_lib/src/client/client.rs @@ -1,6 +1,5 @@ use std::{ - collections::HashMap, - fmt + collections::HashMap }; use serde::{ @@ -39,7 +38,7 @@ impl ClientInfo { let job_result = exec_job(&mut job_meta); let job_data = match job_result.data { Ok(output) => output.multiline(), - Err(e) => e.description() + Err(e) => e.to_string() }; info.insert(job.0.into(), job_data); } @@ -67,14 +66,16 @@ const DEFAULT_JOBS: &[(&str, &str)] = &[ #[cfg(test)] mod tests { use super::*; + use crate::utils::vec_to_string; #[test] fn test_gather() { let cli_info = ClientInfo::gather(); let field = cli_info.get_field("username").unwrap(); + let stdout = JobOutput::from_multiline(field).unwrap().stdout; assert_eq!( - JobOutput::from_multiline(field).unwrap().stdout, - b"plazmoid".to_vec() + &vec_to_string(&stdout), + "plazmoid" ) } diff --git a/lib/u_lib/src/client/network.rs b/lib/u_lib/src/client/network.rs index f45bf3d..a646894 100644 --- a/lib/u_lib/src/client/network.rs +++ b/lib/u_lib/src/client/network.rs @@ -2,6 +2,7 @@ use crate::{ MASTER_SERVER, MASTER_PORT, contracts::*, + UResult }; use reqwest::{ Client, @@ -11,13 +12,24 @@ use reqwest::{ }; use std::{ net::Ipv4Addr, - str::FromStr + str::FromStr, + any::Any }; +pub struct Paths; + +impl Paths { + pub const LIST: &'static str = "list"; + pub const NEW: &'static str = "new"; + //pub const DEL: &'static str = "del"; + pub const JOB_REQ: &'static str = "job_req"; + pub const JOB_ANS: &'static str = "job_ans"; +} + + pub struct ClientHandler { base_url: Url, client: Client, - cli_info: ClientInfo, password: Option } @@ -28,7 +40,6 @@ impl ClientHandler { .unwrap_or(MASTER_SERVER); Self { client: Client::new(), - cli_info: ClientInfo::gather(), base_url: Url::parse( &format!("http://{}:{}", master_server, MASTER_PORT) ).unwrap(), @@ -58,28 +69,90 @@ impl ClientHandler { self.set_pwd(rb) } - pub async fn init(&self) -> RawMsg { // move to result - let response: Response = self.build_post("/new") - .json(&self.cli_info.as_message()) + pub async fn init(&self, param: &ClientInfo) -> UResult { + let response: Response = self.build_post(Paths::NEW) + .json(¶m.as_message()) .send() - .await - .unwrap(); + .await?; let msg = response .json::>() - .await - .unwrap(); - msg.into_item().into_owned() + .await?; + Ok(msg.into_item().into_owned()) } - pub async fn list(&self) -> Vec { - let response: Response = self.build_get("/ls") + pub async fn list(&self) -> UResult> { + let response: Response = self.build_get(Paths::LIST) .send() - .await - .unwrap(); + .await?; let msg = response .json::>>() - .await - .unwrap(); - msg.into_item().into_owned() + .await?; + Ok(msg.into_item().into_owned()) } +/* + pub async fn del(&self) -> UResult<()> { + self.build_post(Paths::DEL).send().await?; + Ok(()) + }*/ } + +// build_handler![path = new, method = "post", param = ClientInfo, result = RawMsg] +// param and result must impl ToMsg +macro_rules! build_handler { + ( + path = $path:tt, + method = $method:literal, + param = $param:ty, + result = $result:ty + ) => { + impl ClientHandler { + pub async fn $path(&self, param: &$param) -> UResult<$result> { + let builder = match $method { + "post" => ClientHandler::build_post, + "get" => ClientHandler::build_get, + _ => panic!("Method '{}' is not allowed", $method) + }; + let base_request = builder(self, stringify!($path)); + let request = if let Some(p) = (param as &dyn Any).downcast_ref::() { + base_request + } else { + base_request.json(¶m.as_message()) + }; + let response = request.send().await?; + let msg = response + .json::>() + .await?; + Ok(msg.into_item().into_owned()) + } + } + + impl Paths { + pub const $path: &'static str = stringify!($path); + } + }; + + ( + path = $path:tt, + method = $method:literal + ) => ( + build_handler!(path = $path, method = $method, param = EmptyMsg, result = EmptyMsg); + ); + + ( + path = $path:tt, + method = $method:literal, + param = $param:ty + ) => ( + build_handler!(path = $path, method = $method, param = $param, result = EmptyMsg); + ); + + ( + path = $path:tt, + method = $method:literal, + result = $result:ty + ) => ( + build_handler!(path = $path, method = $method, param = EmptyMsg, result = $result); + ); +} + +build_handler!(path = del, method = "post"); diff --git a/lib/u_lib/src/contracts/jobs.rs b/lib/u_lib/src/contracts/jobs.rs index cb70cbe..ec5f5a8 100644 --- a/lib/u_lib/src/contracts/jobs.rs +++ b/lib/u_lib/src/contracts/jobs.rs @@ -10,7 +10,7 @@ use serde::{ use uuid::Uuid; //use tokio::process::Command; use super::*; -use crate::UError; +use crate::{UError, UErrType}; #[derive(Serialize, Deserialize, Clone, Debug)] @@ -61,10 +61,9 @@ pub struct JobMeta { impl JobMeta { pub fn from_shell(shell_cmd: String) -> Self { - let uid = Uuid::new_v4(); let job_name = shell_cmd.split(" ").nth(0).unwrap(); Self { - id: uid.clone(), + id: Uuid::new_v4(), name: job_name.to_string(), created: SystemTime::now(), updated: SystemTime::now(), @@ -154,8 +153,8 @@ impl ToMsg for JobResult {} pub struct Job<'meta> { - pub result: JobResult, - pub meta: &'meta mut JobMeta, + result: JobResult, + meta: &'meta mut JobMeta, } impl<'meta> Job<'meta> { @@ -210,7 +209,7 @@ impl<'meta> Job<'meta> { } Err(e) => { self.result.data = Err( - UError::Raw(e.to_string()) + UError::new(UErrType::JobError, Some(e)) ); self.result.retcode = None; } @@ -248,7 +247,10 @@ impl<'meta> Job<'meta> { #[cfg(test)] mod tests { use super::*; - use crate::execute_jobs; + use crate::{ + execute_jobs, + utils::vec_to_string + }; #[test] fn test_shell_job() { @@ -257,8 +259,8 @@ mod tests { execute_jobs(&mut jobs); let job_result = jobs.pop().unwrap().into_result(); assert_eq!( - job_result.data.unwrap().stdout, - b"plazmoid\n".to_vec() + &vec_to_string(&job_result.data.unwrap().stdout), + "plazmoid" ); } @@ -271,4 +273,45 @@ mod tests { assert!(job_result.data.is_err()); assert_eq!(job_result.retcode, None); } + + #[test] + fn test_to_multiline() { + let mut output = JobOutput::new(); + output.stdout = b"lol".to_vec(); + output.stderr = b"kek".to_vec(); + assert_eq!( + output.multiline(), + String::from( + "*** STDOUT ***\n\ + lol\n\ + *** STDERR ***\n\ + kek\n" + ) + ) + } + + #[test] + fn test_to_multiline_stderr_only() { + let mut output = JobOutput::new(); + output.stderr = b"kek".to_vec(); + assert_eq!( + output.multiline(), + String::from( + "*** STDERR ***\n\ + kek\n" + ) + ) + } + + #[test] + fn test_from_multiline() { + let txt = "*** STDOUT ***\n\ + puk\n".to_string(); + let output = JobOutput::from_multiline(&txt).unwrap(); + assert_eq!( + output.stdout, + b"puk".to_vec() + ); + assert_eq!(output.stderr.len(), 0); + } } diff --git a/lib/u_lib/src/contracts/messaging.rs b/lib/u_lib/src/contracts/messaging.rs index 0b27f4a..8ab8182 100644 --- a/lib/u_lib/src/contracts/messaging.rs +++ b/lib/u_lib/src/contracts/messaging.rs @@ -54,7 +54,10 @@ where I: Clone { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct RawMsg(pub String); -impl ToMsg for RawMsg {} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct EmptyMsg; + /* #[cfg(test)] diff --git a/lib/u_lib/src/contracts/mod.rs b/lib/u_lib/src/contracts/mod.rs index 833f09f..4996df4 100644 --- a/lib/u_lib/src/contracts/mod.rs +++ b/lib/u_lib/src/contracts/mod.rs @@ -13,9 +13,11 @@ use std::{ borrow::Cow }; +macro_rules! to_message { + ($($type:ty),+) => { $( + + impl ToMsg for $type {} -macro_rules! to_cow { - ($type:ty) => { impl<'cow> From<$type> for Cow<'cow, $type> { #[inline] fn from(obj: $type) -> Cow<'cow, $type> { @@ -28,9 +30,8 @@ macro_rules! to_cow { fn from(obj: &'cow $type) -> Cow<'cow, $type> { Cow::Borrowed(obj) } - } + } )+ } } -to_cow!(ClientInfo); -to_cow!(RawMsg); \ No newline at end of file +to_message!(ClientInfo, RawMsg, EmptyMsg); \ No newline at end of file diff --git a/lib/u_lib/src/errors.rs b/lib/u_lib/src/errors.rs index e38e167..8d4a65c 100644 --- a/lib/u_lib/src/errors.rs +++ b/lib/u_lib/src/errors.rs @@ -1,27 +1,87 @@ use std::fmt; +use std::error::Error as StdError; +use reqwest::Error as ReqError; use serde::{ Serialize, Deserialize }; -#[derive(Serialize, Deserialize, Clone)] -pub enum UError { +pub type BoxError = Box<(dyn StdError + Send + Sync + 'static)>; +pub type UResult = std::result::Result; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum UErrType { + ConnectionError, + ParseError, + JobError, + Unknown, Raw(String) } +#[derive(Serialize, Deserialize, Clone, Debug)] +struct Inner { + err_type: UErrType, + source: Option +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct UError { + inner: Box +} + impl UError { - pub fn description(&self) -> String { - match self { - UError::Raw(msg) => msg.clone() + pub fn new>(err_type: UErrType, source: Option) -> Self { + Self { + inner: Box::new(Inner { + source: source.map(Into::into), + err_type + }) } } } impl fmt::Debug for UError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let msg = match self { - UError::Raw(msg) => msg + let mut builder = f.debug_struct("errors::UError"); + + builder.field("kind", &self.inner.err_type); + + if let Some(ref source) = self.inner.source { + builder.field("source", source); + } + + builder.finish() + } +} + +impl fmt::Display for UError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let e_type = match self.inner.err_type { + UErrType::Raw(ref msg) => msg, + UErrType::ConnectionError => "Connection error", + UErrType::ParseError => "Parse error", + UErrType::JobError => "Job error", + UErrType::Unknown => "Unknown error", + }; + f.write_str(e_type)?; + + if let Some(ref e) = self.inner.source { + write!(f, ": {}", e) + } else { + Ok(()) + } + } +} + +impl From for UError { + fn from(e: ReqError) -> Self { + let err_type = if e.is_request() { + UErrType::ConnectionError + } else if e.is_decode() { + UErrType::ParseError + } else { + UErrType::Unknown }; - write!(f, "{}", msg) + UError::new(err_type, Some(e)) } } \ No newline at end of file diff --git a/lib/u_lib/src/utils.rs b/lib/u_lib/src/utils.rs index c3e2352..b344dd7 100644 --- a/lib/u_lib/src/utils.rs +++ b/lib/u_lib/src/utils.rs @@ -45,6 +45,11 @@ pub fn setsig(sig: Signal, hnd: SigHandler) { signal(sig, hnd).unwrap(); } } + +pub fn vec_to_string(v: &Vec) -> String { + String::from_utf8_lossy(v).to_string() +} + /* pub fn generate_auth_token() -> String {