use super::JobType; use crate::{models::schema::*, UError, UResult}; use diesel::{Identifiable, Insertable, Queryable}; use guess_host_triple::guess_host_triple; use serde::{Deserialize, Serialize}; use std::fmt; use uuid::Uuid; #[derive(Serialize, Deserialize, Clone, Debug, Queryable, Identifiable, Insertable)] #[table_name = "jobs"] pub struct JobMeta { pub alias: Option, /// string like `bash -c {} -a 1 --arg2`, /// where {} is replaced by executable's tmp path pub argv: String, pub id: Uuid, pub exec_type: JobType, //pub schedule: JobSchedule, pub platform: String, pub payload: Option>, } impl JobMeta { pub fn builder() -> JobMetaBuilder { JobMetaBuilder::default() } pub fn from_shell>(cmd: S) -> UResult { Self::builder().with_shell(cmd).build() } } impl fmt::Display for JobMeta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut out = format!("Job: {}", self.id); if self.alias.is_some() { out += &format!(" ({})", self.alias.as_ref().unwrap()); } out += &format!("\nArgv: {}", self.argv); out += &format!("\nExecutable type: {}", self.exec_type); out += &format!("\nPlatform: {}", self.platform); if self.exec_type == JobType::Shell && self.payload.is_some() { let payload = self.payload.as_ref().unwrap(); let (pld_len, large) = { let pl = payload.len(); if pl > 20 { (20, true) } else { (pl, false) } }; let pld_beginning = payload .iter() .take(pld_len) .map(|u| *u) .collect::>(); out += &format!( "\nPayload: {}{}", String::from_utf8_lossy(&pld_beginning), if large { "" } else { " <...>" } ); } write!(f, "{}", out) } } impl Default for JobMeta { fn default() -> Self { Self { id: Uuid::new_v4(), alias: None, argv: String::new(), exec_type: JobType::Shell, platform: guess_host_triple().unwrap_or("unknown").to_string(), payload: None, } } } pub struct JobMetaBuilder { inner: JobMeta, } impl Default for JobMetaBuilder { fn default() -> Self { Self { inner: JobMeta::default(), } } } impl JobMetaBuilder { pub fn with_shell>(mut self, shell_cmd: S) -> Self { self.inner.argv = shell_cmd.into(); self } pub fn with_payload>>(mut self, payload: C) -> Self { self.inner.payload = Some(payload.into()); self } pub fn with_alias>(mut self, alias: S) -> Self { self.inner.alias = Some(alias.into()); self } pub fn with_type(mut self, e_type: JobType) -> Self { self.inner.exec_type = e_type; self } pub fn build(self) -> UResult { let mut inner = self.inner; match inner.exec_type { JobType::Shell => { if inner.argv == "" { 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 { return Err(empty_err); } match inner.payload.as_ref() { Some(_) => { if !inner.argv.contains("{}") { return Err(UError::JobArgsError( "Argv contains no executable placeholder".into(), )); } else { () } } None => { if inner.argv.contains("{}") { return Err(UError::JobArgsError( "No payload provided, but argv contains executable placeholder" .into(), )); } else { () } } }; Ok(inner) } JobType::Manage => Ok(inner), _ => todo!(), } } /* pub fn from_file(path: PathBuf) -> UResult { let data = fs::read(path) .map_err(|e| UError::FilesystemError( path.to_string_lossy().to_string(), e.to_string() ))?; let filename = path.file_name().unwrap().to_str().unwrap(); }*/ }