From 700154e311e32689c95e43554fc61a7d36199763 Mon Sep 17 00:00:00 2001 From: plazmoid Date: Sun, 3 Oct 2021 17:43:09 +0500 Subject: [PATCH] clippy refactorings --- Makefile.toml | 9 +- bin/u_agent/src/lib.rs | 6 +- bin/u_agent/src/main.rs | 1 - bin/u_panel/Cargo.toml | 3 +- bin/u_panel/src/argparse.rs | 137 +++++++++++++++++++++ bin/u_panel/src/main.rs | 140 +-------------------- bin/u_panel/src/server/app.rs | 23 ++++ bin/u_panel/src/server/mod.rs | 3 + bin/u_server/src/db.rs | 8 +- bin/u_server/src/filters.rs | 4 +- bin/u_server/src/handlers.rs | 14 +-- bin/u_server/src/u_server.rs | 1 + integration/get_docker_ip.sh | 4 + integration/tests/helpers/panel.rs | 14 ++- integration/tests/tests.rs | 16 +++ lib/u_api_proc_macro/src/lib.rs | 2 +- lib/u_lib/src/executor.rs | 2 +- lib/u_lib/src/messaging/base.rs | 5 +- lib/u_lib/src/models/agent.rs | 2 +- lib/u_lib/src/models/jobs/assigned.rs | 27 ++-- lib/u_lib/src/models/jobs/meta.rs | 16 +-- lib/u_lib/src/models/jobs/misc.rs | 149 ---------------------- lib/u_lib/src/utils/combined_result.rs | 2 +- lib/u_lib/src/utils/fmt/stripped.rs | 1 - lib/u_lib/src/utils/mod.rs | 2 + lib/u_lib/src/utils/proc_output.rs | 163 +++++++++++++++++++++++++ scripts/build_musl_libs.sh | 2 +- 27 files changed, 406 insertions(+), 350 deletions(-) create mode 100644 bin/u_panel/src/argparse.rs create mode 100644 bin/u_panel/src/server/app.rs create mode 100644 bin/u_panel/src/server/mod.rs create mode 100755 integration/get_docker_ip.sh create mode 100644 lib/u_lib/src/utils/proc_output.rs diff --git a/Makefile.toml b/Makefile.toml index 256e798..44d8f2d 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -17,15 +17,10 @@ script = "./scripts/build_musl_libs.sh" command = "${CARGO}" args = ["clean"] -[tasks.debug] +[tasks.build] dependencies = ["build_static_libs"] command = "${CARGO}" -args = ["build", "--target", "${TARGET}","${@}"] - -[tasks.release] -dependencies = ["build_static_libs"] -command = "${CARGO}" -args = ["build", "--target", "${TARGET}", "--release", "${@}"] +args = ["build", "--target", "${TARGET}", "${@}"] [tasks.run] script = ''' diff --git a/bin/u_agent/src/lib.rs b/bin/u_agent/src/lib.rs index 946afc9..ad83999 100644 --- a/bin/u_agent/src/lib.rs +++ b/bin/u_agent/src/lib.rs @@ -27,7 +27,7 @@ use u_lib::{ const ITERATION_LATENCY: u64 = 5; pub async fn process_request(job_requests: Vec, client: &ClientHandler) { - if job_requests.len() > 0 { + if !job_requests.is_empty() { for jr in &job_requests { if !JobCache::contains(&jr.job_id) { debug!("Fetching job: {}", &jr.job_id); @@ -53,7 +53,7 @@ pub async fn process_request(job_requests: Vec, client: &ClientHand ); let mut builder = JobBuilder::from_request(job_requests); let errors = builder.pop_errors(); - if errors.len() > 0 { + if !errors.is_empty() { errors.into_iter().for_each(ErrChan::send) } builder.unwrap_one().spawn().await; @@ -86,7 +86,7 @@ async fn do_stuff(client: Arc) -> ! { Err(err) => ErrChan::send(err), } let result: Vec = pop_completed().await.into_iter().collect(); - if result.len() > 0 { + if !result.is_empty() { if let Err(err) = client.report(&result).await { ErrChan::send(err) } diff --git a/bin/u_agent/src/main.rs b/bin/u_agent/src/main.rs index 43166a8..1f3d50a 100644 --- a/bin/u_agent/src/main.rs +++ b/bin/u_agent/src/main.rs @@ -1,4 +1,3 @@ -use tokio; use u_agent::run_forever; #[tokio::main] diff --git a/bin/u_panel/Cargo.toml b/bin/u_panel/Cargo.toml index 1f15786..1746585 100644 --- a/bin/u_panel/Cargo.toml +++ b/bin/u_panel/Cargo.toml @@ -7,7 +7,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = { version = "1.2.0", features = ["macros", "rt-multi-thread", "process"] } structopt = "0.3.21" log = "^0.4" env_logger = "0.7.1" @@ -17,3 +16,5 @@ openssl = "*" u_lib = { version = "*", path = "../../lib/u_lib" } serde_json = "1.0.4" serde = { version = "1.0.114", features = ["derive"] } +actix-web = "3.3.2" +tokio = "1.11.0" diff --git a/bin/u_panel/src/argparse.rs b/bin/u_panel/src/argparse.rs new file mode 100644 index 0000000..9267de4 --- /dev/null +++ b/bin/u_panel/src/argparse.rs @@ -0,0 +1,137 @@ +use std::env; +use std::fmt; +use structopt::StructOpt; +use u_lib::{ + api::ClientHandler, datatypes::DataResult, messaging::AsMsg, models::JobMeta, UError, UResult, +}; +use uuid::Uuid; + +#[derive(StructOpt, Debug)] +pub struct Args { + #[structopt(subcommand)] + cmd: Cmd, + #[structopt(long)] + json: bool, +} + +#[derive(StructOpt, Debug)] +enum Cmd { + Agents(LD), + Jobs(JobALD), + Jobmap(JobMapALD), + Server, +} + +#[derive(StructOpt, Debug)] +enum JobALD { + Add { + #[structopt(long, parse(try_from_str = parse_uuid))] + agent: Option, + + #[structopt(long)] + alias: String, + + #[structopt(subcommand)] + cmd: JobCmd, + }, + #[structopt(flatten)] + LD(LD), +} + +#[derive(StructOpt, Debug)] +enum JobCmd { + #[structopt(external_subcommand)] + Cmd(Vec), +} + +#[derive(StructOpt, Debug)] +enum JobMapALD { + Add { + #[structopt(parse(try_from_str = parse_uuid))] + agent_uid: Uuid, + + job_idents: Vec, + }, + List { + #[structopt(parse(try_from_str = parse_uuid))] + uid: Option, + }, + Delete { + #[structopt(parse(try_from_str = parse_uuid))] + uid: Uuid, + }, +} + +#[derive(StructOpt, Debug)] +enum LD { + List { + #[structopt(parse(try_from_str = parse_uuid))] + uid: Option, + }, + Delete { + #[structopt(parse(try_from_str = parse_uuid))] + uid: Uuid, + }, +} + +fn parse_uuid(src: &str) -> Result { + Uuid::parse_str(src).map_err(|e| e.to_string()) +} + +pub async fn process_cmd(args: Args) -> UResult<()> { + struct Printer { + json: bool, + } + + impl Printer { + pub fn print(&self, data: UResult) { + if self.json { + let data = match data { + Ok(r) => DataResult::Ok(r), + Err(e) => DataResult::Err(e), + }; + println!("{}", serde_json::to_string_pretty(&data).unwrap()); + } else { + match data { + Ok(r) => println!("{}", r), + Err(e) => eprintln!("Error: {}", e), + } + } + } + } + + let token = env::var("ADMIN_AUTH_TOKEN").map_err(|_| UError::WrongToken)?; + let cli_handler = ClientHandler::new(None).password(token); + let printer = Printer { json: args.json }; + match args.cmd { + Cmd::Agents(action) => match action { + LD::List { uid } => printer.print(cli_handler.get_agents(uid).await), + LD::Delete { uid } => printer.print(cli_handler.del(Some(uid)).await), + }, + Cmd::Jobs(action) => match action { + JobALD::Add { + cmd: JobCmd::Cmd(cmd), + alias, + .. + } => { + let job = JobMeta::builder() + .with_shell(cmd.join(" ")) + .with_alias(alias) + .build()?; + printer.print(cli_handler.upload_jobs(&[job]).await); + } + JobALD::LD(LD::List { uid }) => printer.print(cli_handler.get_jobs(uid).await), + JobALD::LD(LD::Delete { uid }) => printer.print(cli_handler.del(Some(uid)).await), + }, + Cmd::Jobmap(action) => match action { + JobMapALD::Add { + agent_uid, + job_idents, + } => printer.print(cli_handler.set_jobs(Some(agent_uid), &job_idents).await), + JobMapALD::List { uid } => printer.print(cli_handler.get_agent_jobs(uid).await), + JobMapALD::Delete { uid } => printer.print(cli_handler.del(Some(uid)).await), + }, + Cmd::Server => crate::server::serve().unwrap(), + } + Ok(()) +} diff --git a/bin/u_panel/src/main.rs b/bin/u_panel/src/main.rs index 39f0a7b..828a67f 100644 --- a/bin/u_panel/src/main.rs +++ b/bin/u_panel/src/main.rs @@ -1,140 +1,10 @@ -use std::env; -use std::fmt; +mod argparse; +mod server; + +use argparse::{process_cmd, Args}; use std::process; use structopt::StructOpt; -use u_lib::{ - api::ClientHandler, datatypes::DataResult, messaging::AsMsg, models::JobMeta, utils::init_env, - UError, UResult, -}; -use uuid::Uuid; - -#[derive(StructOpt, Debug)] -struct Args { - #[structopt(subcommand)] - cmd: Cmd, - #[structopt(long)] - json: bool, -} - -#[derive(StructOpt, Debug)] -enum Cmd { - Agents(LD), - Jobs(JobALD), - Jobmap(JobMapALD), -} - -#[derive(StructOpt, Debug)] -enum JobALD { - Add { - #[structopt(long, parse(try_from_str = parse_uuid))] - agent: Option, - - #[structopt(long)] - alias: String, - - #[structopt(subcommand)] - cmd: JobCmd, - }, - #[structopt(flatten)] - LD(LD), -} - -#[derive(StructOpt, Debug)] -enum JobCmd { - #[structopt(external_subcommand)] - Cmd(Vec), -} - -#[derive(StructOpt, Debug)] -enum JobMapALD { - Add { - #[structopt(parse(try_from_str = parse_uuid))] - agent_uid: Uuid, - - job_idents: Vec, - }, - List { - #[structopt(parse(try_from_str = parse_uuid))] - uid: Option, - }, - Delete { - #[structopt(parse(try_from_str = parse_uuid))] - uid: Uuid, - }, -} - -#[derive(StructOpt, Debug)] -enum LD { - List { - #[structopt(parse(try_from_str = parse_uuid))] - uid: Option, - }, - Delete { - #[structopt(parse(try_from_str = parse_uuid))] - uid: Uuid, - }, -} - -fn parse_uuid(src: &str) -> Result { - Uuid::parse_str(src).map_err(|e| e.to_string()) -} - -async fn process_cmd(args: Args) -> UResult<()> { - struct Printer { - json: bool, - } - - impl Printer { - pub fn print(&self, data: UResult) { - if self.json { - let data = match data { - Ok(r) => DataResult::Ok(r), - Err(e) => DataResult::Err(e), - }; - println!("{}", serde_json::to_string_pretty(&data).unwrap()); - } else { - match data { - Ok(r) => println!("{}", r), - Err(e) => eprintln!("Error: {}", e), - } - } - } - } - - let token = env::var("ADMIN_AUTH_TOKEN").map_err(|_| UError::WrongToken)?; - let cli_handler = ClientHandler::new(None).password(token); - let printer = Printer { json: args.json }; - match args.cmd { - Cmd::Agents(action) => match action { - LD::List { uid } => printer.print(cli_handler.get_agents(uid).await), - LD::Delete { uid } => printer.print(cli_handler.del(Some(uid)).await), - }, - Cmd::Jobs(action) => match action { - JobALD::Add { - cmd: JobCmd::Cmd(cmd), - alias, - agent: _agent, - } => { - let job = JobMeta::builder() - .with_shell(cmd.join(" ")) - .with_alias(alias) - .build()?; - printer.print(cli_handler.upload_jobs(&[job]).await); - } - JobALD::LD(LD::List { uid }) => printer.print(cli_handler.get_jobs(uid).await), - JobALD::LD(LD::Delete { uid }) => printer.print(cli_handler.del(Some(uid)).await), - }, - Cmd::Jobmap(action) => match action { - JobMapALD::Add { - agent_uid, - job_idents, - } => printer.print(cli_handler.set_jobs(Some(agent_uid), &job_idents).await), - JobMapALD::List { uid } => printer.print(cli_handler.get_agent_jobs(uid).await), - JobMapALD::Delete { uid } => printer.print(cli_handler.del(Some(uid)).await), - }, - } - Ok(()) -} +use u_lib::utils::init_env; #[tokio::main] async fn main() { diff --git a/bin/u_panel/src/server/app.rs b/bin/u_panel/src/server/app.rs new file mode 100644 index 0000000..d7c5117 --- /dev/null +++ b/bin/u_panel/src/server/app.rs @@ -0,0 +1,23 @@ +/* +Tabs: Agents, Tasks, Summary +every tab has list page and item page with more info/actions + +Agents: + +| id | alias | ..see struct | tasks done +| stripped | alias | ... | clickable number of assigned jobs + +almost all fields are editable, rows are deletable + + +*/ + +use actix_web::{web, App, HttpResponse, HttpServer}; + +#[actix_web::main] +pub async fn serve() -> std::io::Result<()> { + let addr = "127.0.0.1:8080"; + let app = || App::new().route("/", web::get().to(|| HttpResponse::Ok().body("ok"))); + println!("Serving at http://{}", addr); + HttpServer::new(app).bind(addr)?.run().await +} diff --git a/bin/u_panel/src/server/mod.rs b/bin/u_panel/src/server/mod.rs new file mode 100644 index 0000000..a4fad6b --- /dev/null +++ b/bin/u_panel/src/server/mod.rs @@ -0,0 +1,3 @@ +mod app; + +pub use app::serve; diff --git a/bin/u_server/src/db.rs b/bin/u_server/src/db.rs index d5db645..c2a2af1 100644 --- a/bin/u_server/src/db.rs +++ b/bin/u_server/src/db.rs @@ -151,7 +151,7 @@ impl UDB { } }) .collect::>(); - if not_found_jobs.len() > 0 { + if !not_found_jobs.is_empty() { return Err(ULocalError::NotFound(not_found_jobs.join(", "))); } let job_requests = job_uids @@ -172,7 +172,7 @@ impl UDB { Ok(assigned_uids) } - pub fn del_jobs(&self, uids: &Vec) -> ULocalResult { + pub fn del_jobs(&self, uids: &[Uuid]) -> ULocalResult { use schema::jobs; let mut affected = 0; for &uid in uids { @@ -184,7 +184,7 @@ impl UDB { Ok(affected) } - pub fn del_results(&self, uids: &Vec) -> ULocalResult { + pub fn del_results(&self, uids: &[Uuid]) -> ULocalResult { use schema::results; let mut affected = 0; for &uid in uids { @@ -196,7 +196,7 @@ impl UDB { Ok(affected) } - pub fn del_agents(&self, uids: &Vec) -> ULocalResult { + pub fn del_agents(&self, uids: &[Uuid]) -> ULocalResult { use schema::agents; let mut affected = 0; for &uid in uids { diff --git a/bin/u_server/src/filters.rs b/bin/u_server/src/filters.rs index 20ba803..44b40b8 100644 --- a/bin/u_server/src/filters.rs +++ b/bin/u_server/src/filters.rs @@ -48,12 +48,12 @@ pub fn make_filters() -> impl Filter .map(Some) .or_else(infallible_none), ) - .and_then(|uid| Endpoints::get_agent_jobs(uid)); + .and_then(Endpoints::get_agent_jobs); let get_personal_jobs = warp::get() .and(warp::path("get_personal_jobs")) .and(warp::path::param::().map(Some)) - .and_then(|uid| Endpoints::get_personal_jobs(uid)); + .and_then(Endpoints::get_personal_jobs); let del = warp::get() .and(warp::path("del")) diff --git a/bin/u_server/src/handlers.rs b/bin/u_server/src/handlers.rs index cdf9e47..3158bcf 100644 --- a/bin/u_server/src/handlers.rs +++ b/bin/u_server/src/handlers.rs @@ -46,7 +46,7 @@ impl Endpoints { info!("hnd: get_agents"); UDB::lock_db() .get_agents(uid) - .map(|m| build_message(m)) + .map(build_message) .or_else(|e| Ok(build_err(e))) } @@ -54,7 +54,7 @@ impl Endpoints { info!("hnd: get_jobs"); UDB::lock_db() .get_jobs(uid) - .map(|m| build_message(m)) + .map(build_message) .or_else(|e| Ok(build_err(e))) } @@ -62,14 +62,14 @@ impl Endpoints { info!("hnd: get_agent_jobs"); UDB::lock_db() .get_exact_jobs(uid, false) - .map(|m| build_message(m)) + .map(build_message) .or_else(|e| Ok(build_err(e))) } pub async fn get_personal_jobs(uid: Option) -> Result, Rejection> { info!("hnd: get_personal_jobs"); let agents = UDB::lock_db().get_agents(uid).unwrap(); - if agents.len() == 0 { + if agents.is_empty() { let db = UDB::lock_db(); db.insert_agent(&Agent::with_id(uid.unwrap())).unwrap(); let job = db.find_job_by_alias("agent_hello").unwrap(); @@ -105,7 +105,7 @@ impl Endpoints { let db = UDB::lock_db(); let del_fns = &[UDB::del_agents, UDB::del_jobs, UDB::del_results]; for del_fn in del_fns { - let affected = del_fn(&db, &vec![uid]).unwrap(); + let affected = del_fn(&db, &[uid]).unwrap(); if affected > 0 { return Ok(build_message(affected as i32)); } @@ -130,7 +130,7 @@ impl Endpoints { match jobs { Ok(j) => UDB::lock_db() .set_jobs_for_agent(&agent_uid, &j) - .map(|assigned_uids| build_message(assigned_uids)) + .map(build_message) .or_else(|e| Ok(build_err(e))), Err(e) => Ok(build_err(e)), } @@ -172,7 +172,7 @@ impl Endpoints { Reportable::Dummy => (), } } - if failed.len() > 0 { + if !failed.is_empty() { let err_msg = ULocalError::ProcessingError(failed.join(", ")); return Ok(build_err(err_msg)); } diff --git a/bin/u_server/src/u_server.rs b/bin/u_server/src/u_server.rs index 71d8185..2317631 100644 --- a/bin/u_server/src/u_server.rs +++ b/bin/u_server/src/u_server.rs @@ -59,6 +59,7 @@ fn init_all() { prefill_jobs(); } +//TODO: tracing-subscriber pub async fn serve() { init_all(); let routes = make_filters(); diff --git a/integration/get_docker_ip.sh b/integration/get_docker_ip.sh new file mode 100755 index 0000000..9406e5e --- /dev/null +++ b/integration/get_docker_ip.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +PAT=".+IPAddress\W+\"([0-9\.]+).+" +docker ps | grep unki/$1 | cut -d' ' -f1 | xargs docker inspect | grep -P $PAT | sed -r "s/$PAT/\1/" diff --git a/integration/tests/helpers/panel.rs b/integration/tests/helpers/panel.rs index 713c60a..929715a 100644 --- a/integration/tests/helpers/panel.rs +++ b/integration/tests/helpers/panel.rs @@ -1,8 +1,12 @@ use serde::de::DeserializeOwned; use serde_json::{from_slice, Value}; use shlex::split; +use std::fmt::Display; use std::process::{Command, Output}; -use u_lib::{datatypes::DataResult, utils::VecDisplay}; +use u_lib::{ + datatypes::DataResult, + utils::{bytes_to_string, ProcOutput}, +}; const PANEL_BINARY: &str = "/u_panel"; @@ -21,10 +25,11 @@ impl Panel { pub fn output_argv(argv: &[&str]) -> PanelResult { let result = Self::run(argv); - from_slice(&result.stdout).map_err(|e| { + let output = ProcOutput::from_output(&result).to_appropriate(); + from_slice(&output).map_err(|e| { eprintln!( "Failed to decode panel response: '{}'", - VecDisplay(result.stdout) + bytes_to_string(&output) ); e.to_string() }) @@ -48,7 +53,8 @@ impl Panel { } } - pub fn check_status(args: impl Into) { + pub fn check_status(args: impl Into + Display) { + println!("Panel: executing '{}'", &args); let result: PanelResult = Self::output(args); Self::status_is_ok(result); } diff --git a/integration/tests/tests.rs b/integration/tests/tests.rs index 7ef28c7..35a4789 100644 --- a/integration/tests/tests.rs +++ b/integration/tests/tests.rs @@ -2,5 +2,21 @@ mod behaviour; mod fixtures; mod helpers; +use std::env; + #[macro_use] extern crate rstest; + +// TODO: huita +#[ignore] +#[tokio::test] +async fn test_non_auth_connection_dropped() { + let env_server = env::var("U_SERVER").unwrap(); + match reqwest::get(format!("https://{}", env_server)).await { + Err(e) => { + dbg!(e.to_string()); + assert!(e.is_request()) + } + _ => panic!("no error occured on foreign client connection"), + } +} diff --git a/lib/u_api_proc_macro/src/lib.rs b/lib/u_api_proc_macro/src/lib.rs index f3d3a94..1a400ab 100644 --- a/lib/u_api_proc_macro/src/lib.rs +++ b/lib/u_api_proc_macro/src/lib.rs @@ -108,7 +108,7 @@ fn parse_fn_args(raw: Punctuated) -> FnArgs { if &arg_name != "url_param" && &arg_name != "payload" { panic!("Wrong arg name: {}", &arg_name) } - let arg_type = *argt.ty.clone(); + let arg_type = *argt.ty; Some((arg_name, arg_type)) } else { None diff --git a/lib/u_lib/src/executor.rs b/lib/u_lib/src/executor.rs index dc4bc79..c34e08b 100644 --- a/lib/u_lib/src/executor.rs +++ b/lib/u_lib/src/executor.rs @@ -118,7 +118,7 @@ pub async fn pop_completed() -> Vec { .lock() .await .keys() - .map(|k| *k) + .copied() .collect::>(); for fid in fids { if let Some(r) = pop_task_if_completed(fid).await { diff --git a/lib/u_lib/src/messaging/base.rs b/lib/u_lib/src/messaging/base.rs index 5330927..8ac1158 100644 --- a/lib/u_lib/src/messaging/base.rs +++ b/lib/u_lib/src/messaging/base.rs @@ -46,10 +46,7 @@ impl<'cow, I: AsMsg> BaseMessage<'cow, I> { C: Into>, { let Moo(inner) = inner.into(); - Self { - id: UID.clone(), - inner, - } + Self { id: *UID, inner } } pub fn into_inner(self) -> I { diff --git a/lib/u_lib/src/models/agent.rs b/lib/u_lib/src/models/agent.rs index bcc46e9..daa49a0 100644 --- a/lib/u_lib/src/models/agent.rs +++ b/lib/u_lib/src/models/agent.rs @@ -106,7 +106,7 @@ impl Default for Agent { fn default() -> Self { Self { alias: None, - id: UID.clone(), + id: *UID, hostname: String::new(), is_root: false, is_root_allowed: false, diff --git a/lib/u_lib/src/models/jobs/assigned.rs b/lib/u_lib/src/models/jobs/assigned.rs index a75d88c..1b0b3a9 100644 --- a/lib/u_lib/src/models/jobs/assigned.rs +++ b/lib/u_lib/src/models/jobs/assigned.rs @@ -3,13 +3,13 @@ use crate::{ cache::JobCache, errors::UError, messaging::Reportable, - models::{schema::*, JobOutput}, - utils::{systime_to_string, TempFile}, + models::schema::*, + utils::{systime_to_string, ProcOutput, TempFile}, UID, }; use diesel::{Identifiable, Insertable, Queryable}; use serde::{Deserialize, Serialize}; -use std::{fmt, process::Output, time::SystemTime}; +use std::{fmt, time::SystemTime}; use tokio::process::Command; use uuid::Uuid; @@ -101,19 +101,12 @@ impl AssignedJob { let args = split_cmd.collect::>(); let cmd_result = Command::new(cmd).args(args).output().await; let (data, retcode) = match cmd_result { - Ok(Output { - status, - stdout, - stderr, - }) => ( - JobOutput::new() - .stdout(stdout) - .stderr(stderr) - .into_combined(), - status.code(), + Ok(output) => ( + ProcOutput::from_output(&output).into_combined(), + output.status.code(), ), Err(e) => ( - JobOutput::new() + ProcOutput::new() .stderr(e.to_string().into_bytes()) .into_combined(), None, @@ -134,15 +127,15 @@ impl AssignedJob { } } - pub fn as_job_output(&self) -> Option { + pub fn as_job_output(&self) -> Option { self.result .as_ref() - .and_then(|r| JobOutput::from_combined(r)) + .and_then(|r| ProcOutput::from_combined(r)) } pub fn to_raw_result(&self) -> Vec { match self.result.as_ref() { - Some(r) => match JobOutput::from_combined(r) { + Some(r) => match ProcOutput::from_combined(r) { Some(o) => o.to_appropriate(), None => r.clone(), }, diff --git a/lib/u_lib/src/models/jobs/meta.rs b/lib/u_lib/src/models/jobs/meta.rs index 79bce50..2a12e06 100644 --- a/lib/u_lib/src/models/jobs/meta.rs +++ b/lib/u_lib/src/models/jobs/meta.rs @@ -26,7 +26,7 @@ impl JobMeta { JobMetaBuilder::default() } - pub fn from_shell>(cmd: S) -> UResult { + pub fn from_shell(cmd: impl Into) -> UResult { Self::builder().with_shell(cmd).build() } } @@ -80,17 +80,17 @@ impl Default for JobMetaBuilder { } impl JobMetaBuilder { - pub fn with_shell>(mut self, shell_cmd: S) -> Self { + pub fn with_shell(mut self, shell_cmd: impl Into) -> Self { self.inner.argv = shell_cmd.into(); self } - pub fn with_payload>>(mut self, payload: C) -> Self { + pub fn with_payload(mut self, payload: impl Into>) -> Self { self.inner.payload = Some(payload.into()); self } - pub fn with_alias>(mut self, alias: S) -> Self { + pub fn with_alias(mut self, alias: impl Into) -> Self { self.inner.alias = Some(alias.into()); self } @@ -104,13 +104,13 @@ impl JobMetaBuilder { let mut inner = self.inner; match inner.exec_type { JobType::Shell => { - if inner.argv == "" { + if inner.argv.is_empty() { inner.argv = String::from("/bin/bash -c {}") } let argv_parts = shlex::split(&inner.argv).ok_or(UError::JobArgsError("Shlex failed".into()))?; let empty_err = UError::JobArgsError("Empty argv".into()); - if argv_parts.get(0).ok_or(empty_err.clone())?.len() == 0 { + if argv_parts.get(0).ok_or(empty_err.clone())?.is_empty() { return Err(empty_err.into()); } match inner.payload.as_ref() { @@ -120,8 +120,6 @@ impl JobMetaBuilder { "Argv contains no executable placeholder".into(), ) .into()); - } else { - () } } None => { @@ -131,8 +129,6 @@ impl JobMetaBuilder { .into(), ) .into()); - } else { - () } } }; diff --git a/lib/u_lib/src/models/jobs/misc.rs b/lib/u_lib/src/models/jobs/misc.rs index 7451548..ebcd38d 100644 --- a/lib/u_lib/src/models/jobs/misc.rs +++ b/lib/u_lib/src/models/jobs/misc.rs @@ -35,152 +35,3 @@ pub enum JobType { Shell, Python, } - -#[derive(Clone, Debug)] -pub struct JobOutput { - pub stdout: Vec, - pub stderr: Vec, -} - -impl JobOutput { - const STREAM_BORDER: &'static str = "***"; - const STDOUT: &'static str = "STDOUT"; - const STDERR: &'static str = "STDERR"; - - #[inline] - fn create_delim(header: &'static str) -> Vec { - format!( - "<{border}{head}{border}>", - border = Self::STREAM_BORDER, - head = header - ) - .into_bytes() - } - - pub fn new() -> Self { - Self { - stdout: vec![], - stderr: vec![], - } - } - - pub fn stdout(mut self, data: Vec) -> Self { - self.stdout = data; - self - } - - pub fn stderr(mut self, data: Vec) -> Self { - self.stderr = data; - self - } - - pub fn into_combined(self) -> Vec { - let mut result: Vec = vec![]; - if self.stdout.len() > 0 { - result.extend(Self::create_delim(Self::STDOUT)); - result.extend(self.stdout); - } - - if self.stderr.len() > 0 { - result.extend(Self::create_delim(Self::STDERR)); - result.extend(self.stderr); - } - result - } - - pub fn from_combined(raw: &[u8]) -> Option { - enum ParseFirst { - Stdout, - Stderr, - } - fn split_by_subslice<'s>(slice: &'s [u8], subslice: &[u8]) -> Option<(&'s [u8], &'s [u8])> { - slice - .windows(subslice.len()) - .position(|w| w == subslice) - .map(|split_pos| { - let splitted = slice.split_at(split_pos); - (&splitted.0[..split_pos], &splitted.1[subslice.len()..]) - }) - } - let splitter = |p: ParseFirst| { - let (first_hdr, second_hdr) = match p { - ParseFirst::Stdout => (Self::STDOUT, Self::STDERR), - ParseFirst::Stderr => (Self::STDERR, Self::STDOUT), - }; - let first_hdr = Self::create_delim(first_hdr); - let second_hdr = Self::create_delim(second_hdr); - split_by_subslice(raw, &first_hdr).map(|(_, p2)| { - match split_by_subslice(p2, &second_hdr) { - Some((p2_1, p2_2)) => Self::new().stdout(p2_1.to_vec()).stderr(p2_2.to_vec()), - None => Self::new().stdout(p2.to_vec()), - } - }) - }; - splitter(ParseFirst::Stdout).or(splitter(ParseFirst::Stderr)) - } - - pub fn to_appropriate(&self) -> Vec { - let mut result: Vec = vec![]; - if self.stdout.len() > 0 { - result.extend(&self.stdout); - } - if self.stderr.len() > 0 { - if result.len() > 0 { - result.push(b'\n'); - } - result.extend(&self.stderr); - } - if result.len() == 0 { - result.extend(b"No data"); - } - result - } -} - -#[cfg(test)] -mod tests { - use crate::{models::JobOutput, utils::bytes_to_string}; - use test_case::test_case; - - const STDOUT: &str = "<***STDOUT***>"; - const STDERR: &str = "<***STDERR***>"; - - #[test_case( - "lol", - "kek", - &format!("{}lol{}kek", STDOUT, STDERR) - ;"stdout stderr" - )] - #[test_case( - "", - "kek", - &format!("{}kek", STDERR) - ;"stderr" - )] - fn test_to_combined(stdout: &str, stderr: &str, result: &str) { - let output = JobOutput::new() - .stdout(stdout.as_bytes().to_vec()) - .stderr(stderr.as_bytes().to_vec()); - assert_eq!(&bytes_to_string(&output.into_combined()), result) - } - - #[test_case( - &format!("{}lal{}kik", STDOUT, STDERR), - "lal\nkik" - ;"stdout stderr" - )] - #[test_case( - &format!("{}qeq", STDOUT), - "qeq" - ;"stdout" - )] - #[test_case( - &format!("{}vev", STDERR), - "vev" - ;"stderr" - )] - fn test_from_combined(src: &str, result: &str) { - let output = JobOutput::from_combined(src.as_bytes()).unwrap(); - assert_eq!(bytes_to_string(&output.to_appropriate()).trim(), result); - } -} diff --git a/lib/u_lib/src/utils/combined_result.rs b/lib/u_lib/src/utils/combined_result.rs index aa0968d..4464c39 100644 --- a/lib/u_lib/src/utils/combined_result.rs +++ b/lib/u_lib/src/utils/combined_result.rs @@ -31,7 +31,7 @@ impl CombinedResult { } pub fn has_err(&self) -> bool { - self.err.len() > 0 + !self.err.is_empty() } pub fn unwrap_one(self) -> T { diff --git a/lib/u_lib/src/utils/fmt/stripped.rs b/lib/u_lib/src/utils/fmt/stripped.rs index 8681a97..b1ed949 100644 --- a/lib/u_lib/src/utils/fmt/stripped.rs +++ b/lib/u_lib/src/utils/fmt/stripped.rs @@ -6,7 +6,6 @@ use std::str::Chars; const MAX_DATA_LEN: usize = 200; pub trait Strippable { - //TODO: waiting for stabilizing GATs type Item: fmt::Display; type TypeIter: Iterator; diff --git a/lib/u_lib/src/utils/mod.rs b/lib/u_lib/src/utils/mod.rs index 98853c2..41cf1a2 100644 --- a/lib/u_lib/src/utils/mod.rs +++ b/lib/u_lib/src/utils/mod.rs @@ -2,6 +2,7 @@ mod combined_result; mod conv; mod fmt; mod misc; +mod proc_output; mod storage; mod tempfile; mod vec_display; @@ -10,6 +11,7 @@ pub use combined_result::*; pub use conv::*; pub use fmt::*; pub use misc::*; +pub use proc_output::*; pub use storage::*; pub use tempfile::*; pub use vec_display::*; diff --git a/lib/u_lib/src/utils/proc_output.rs b/lib/u_lib/src/utils/proc_output.rs new file mode 100644 index 0000000..5236e6b --- /dev/null +++ b/lib/u_lib/src/utils/proc_output.rs @@ -0,0 +1,163 @@ +use std::process::Output; + +#[derive(Clone, Debug)] +pub struct ProcOutput { + pub stdout: Vec, + pub stderr: Vec, +} + +impl ProcOutput { + const STREAM_BORDER: &'static str = "***"; + const STDOUT: &'static str = "STDOUT"; + const STDERR: &'static str = "STDERR"; + + #[inline] + fn create_delim(header: &'static str) -> Vec { + format!( + "<{border}{head}{border}>", + border = Self::STREAM_BORDER, + head = header + ) + .into_bytes() + } + + pub fn from_output(output: &Output) -> Self { + let mut this = Self::new().stdout(output.stdout.to_vec()); + if !output.status.success() && output.stderr.len() > 0 { + this.stderr = output.stderr.to_vec(); + } + this + } + + pub fn new() -> Self { + Self { + stdout: vec![], + stderr: vec![], + } + } + + pub fn stdout(mut self, data: Vec) -> Self { + self.stdout = data; + self + } + + pub fn stderr(mut self, data: Vec) -> Self { + self.stderr = data; + self + } + + /// Make bytestring like '<***STDOUT***>...<***STDERR***>...' + pub fn into_combined(self) -> Vec { + let mut result: Vec = vec![]; + if !self.stdout.is_empty() { + result.extend(Self::create_delim(Self::STDOUT)); + result.extend(self.stdout); + } + + if !self.stderr.is_empty() { + result.extend(Self::create_delim(Self::STDERR)); + result.extend(self.stderr); + } + result + } + + pub fn from_combined(raw: &[u8]) -> Option { + enum ParseFirst { + Stdout, + Stderr, + } + fn split_by_subslice<'s>(slice: &'s [u8], subslice: &[u8]) -> Option<(&'s [u8], &'s [u8])> { + slice + .windows(subslice.len()) + .position(|w| w == subslice) + .map(|split_pos| { + let splitted = slice.split_at(split_pos); + (&splitted.0[..split_pos], &splitted.1[subslice.len()..]) + }) + } + let splitter = |p: ParseFirst| { + let (first_hdr, second_hdr) = match p { + ParseFirst::Stdout => (Self::STDOUT, Self::STDERR), + ParseFirst::Stderr => (Self::STDERR, Self::STDOUT), + }; + let first_hdr = Self::create_delim(first_hdr); + let second_hdr = Self::create_delim(second_hdr); + split_by_subslice(raw, &first_hdr).map(|(_, p2)| { + match split_by_subslice(p2, &second_hdr) { + Some((p2_1, p2_2)) => Self::new().stdout(p2_1.to_vec()).stderr(p2_2.to_vec()), + None => Self::new().stdout(p2.to_vec()), + } + }) + }; + splitter(ParseFirst::Stdout).or_else(|| splitter(ParseFirst::Stderr)) + } + + /// Chooses between stdout and stderr or both wisely + pub fn to_appropriate(&self) -> Vec { + let mut result: Vec = vec![]; + let mut altered = false; + if !self.stdout.is_empty() { + result.extend(&self.stdout); + altered = true; + } + if !self.stderr.is_empty() { + if altered { + result.push(b'\n'); + } + result.extend(&self.stderr); + altered = true; + } + if !altered { + result.extend(b"No data"); + } + result + } +} + +#[cfg(test)] +mod tests { + use crate::utils::{bytes_to_string, ProcOutput}; + use test_case::test_case; + + const STDOUT: &str = "<***STDOUT***>"; + const STDERR: &str = "<***STDERR***>"; + + #[test_case( + "lol", + "kek", + &format!("{}lol{}kek", STDOUT, STDERR) + ;"stdout stderr" + )] + #[test_case( + "", + "kek", + &format!("{}kek", STDERR) + ;"stderr" + )] + fn test_to_combined(stdout: &str, stderr: &str, result: &str) { + let output = ProcOutput::new() + .stdout(stdout.as_bytes().to_vec()) + .stderr(stderr.as_bytes().to_vec()); + assert_eq!(&bytes_to_string(&output.into_combined()), result) + } + + #[test_case( + &format!("{}lal{}kik", STDOUT, STDERR), + "lal\nkik" + ;"stdout stderr" + )] + #[test_case( + &format!("{}qeq", STDOUT), + "qeq" + ;"stdout" + )] + #[test_case( + &format!("{}vev", STDERR), + "vev" + ;"stderr" + )] + fn test_from_combined(src: &str, result: &str) { + let output = ProcOutput::from_combined(src.as_bytes()).unwrap(); + assert_eq!(bytes_to_string(&output.to_appropriate()).trim(), result); + } +} diff --git a/scripts/build_musl_libs.sh b/scripts/build_musl_libs.sh index 1cf612c..b63211d 100755 --- a/scripts/build_musl_libs.sh +++ b/scripts/build_musl_libs.sh @@ -2,7 +2,7 @@ set -ex source $(dirname $0)/rootdir.sh #set ROOTDIR ARGS=$@ -STATIC_LIBS=static +STATIC_LIBS=./static DOCKER_EXCHG=/musl-share IMAGE=unki/musllibs mkdir -p $STATIC_LIBS