186 lines
4.9 KiB
186 lines
4.9 KiB
use diesel_derive_enum::DbEnum; |
|
use serde::{Deserialize, Serialize}; |
|
use strum::Display; |
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)] |
|
pub enum ManageAction { |
|
Ping, |
|
UpdateAvailable, |
|
JobsResultsRequest, |
|
Terminate, |
|
} |
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] |
|
pub enum JobSchedule { |
|
Once, |
|
Permanent, |
|
//Scheduled |
|
} |
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, DbEnum, Display)] |
|
#[PgType = "JobState"] |
|
#[DieselType = "Jobstate"] |
|
pub enum JobState { |
|
Queued, // server created a job, but client didn't get it yet |
|
//Pending, // client got a job, but not running yet |
|
Running, // client is currently running a job |
|
Finished, |
|
} |
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, DbEnum, Display)] |
|
#[PgType = "JobType"] |
|
#[DieselType = "Jobtype"] |
|
pub enum JobType { |
|
Manage, |
|
Shell, |
|
Python, |
|
} |
|
|
|
#[derive(Clone, Debug)] |
|
pub struct JobOutput { |
|
pub stdout: Vec<u8>, |
|
pub stderr: Vec<u8>, |
|
} |
|
|
|
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<u8> { |
|
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<u8>) -> Self { |
|
self.stdout = data; |
|
self |
|
} |
|
|
|
pub fn stderr(mut self, data: Vec<u8>) -> Self { |
|
self.stderr = data; |
|
self |
|
} |
|
|
|
pub fn into_combined(self) -> Vec<u8> { |
|
let mut result: Vec<u8> = 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<Self> { |
|
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<u8> { |
|
let mut result: Vec<u8> = 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); |
|
} |
|
}
|
|
|