Newer
Older
use std::path::Path;
use std::collections::{HashMap, hash_map};
use std::io::Error as IOError;
use tera::{Context, Tera};
#[cfg(not(tarpaulin_include))]
let split = path.split('/');
let split_vec = split.collect::<Vec<&str>>();
split_vec[split_vec.len()-1].to_string()
}
#[cfg(not(tarpaulin_include))]
pub fn open_files(dir_path: &Path) -> Result<Vec<String>, IOError> {
let files = fs::read_dir(dir_path).unwrap();
let mut conf_files = Vec::new();
for file in files {
let conf_file = match fs::read_to_string(file.unwrap().path()) {
Ok(file) => file,
Err(err) => return Err(err)
};
}
Ok(conf_files)
}
#[cfg(not(tarpaulin_include))]
pub fn parse_yaml(conf_file: String) -> Result<JobConfCommand, serde_yaml::Error> {
let conf: JobConfCommand = match serde_yaml::from_str(&conf_file) {
Ok(file) => file,
Err(err) => return Err(err)
Ok(conf)
}
pub fn check_syntax(conf: &JobConfCommand) -> Result<&JobConfCommand, JobConfError> {
for param in &conf.parameters {
match ¶m.default_value {
Some(val) => {
match param.value_type {
JobConfValueType::Boolean => {
if val.parse::<bool>().is_err() {
eprintln!("Unable to cast default-value to string");
return Err(JobConfError::ParseBoolError);
}
}
JobConfValueType::Integer => {
if val.parse::<i32>().is_err() {
eprintln!("Unable to cast default-value to string");
return Err(JobConfError::ParseIntError);
}
}
JobConfValueType::Float => {
if val.parse::<f64>().is_err() {
eprintln!("Unable to cast default-value to string");
return Err(JobConfError::ParseFloatError);
JobConfValueType::None => ()
},
None => ()
}
if param.named && param.parameter_name.is_none(){
eprintln!("Missing expected parameter name (named parameter is set to true)");
return Err(JobConfError::MissingValue);
#[cfg(not(tarpaulin_include))]
pub fn load_conf_files(root_dir: &Path) -> Result<Configurations, AdapterError> {
let mut parsed_configurations = Configurations::new();
let softwares_dir = match fs::read_dir(root_dir) {
Ok(result) => result,
Err(err) => return Err(AdapterError {kind: AdapterErrorKind::IOError, error:Some(Box::new(err))}),
};
for dir in softwares_dir {
let dirname = dir.unwrap();
let version_dir = match fs::read_dir(&dirname.path()) {
Ok(result) => result,
Err(err) => return Err(AdapterError {kind: AdapterErrorKind::IOError, error:Some(Box::new(err))}),
};
for vdir in version_dir {
let vdirname = vdir.unwrap();
let job_conf = match fs::read_dir(&vdirname.path()) {
Ok(result) => result,
Err(err) => return Err(AdapterError {kind: AdapterErrorKind::IOError, error:Some(Box::new(err))}),
};
for conf in job_conf {
let filename = conf.unwrap();
let content = match fs::read_to_string(&filename.path()) {
Ok(file) => file,
Err(err) => return Err(AdapterError {kind: AdapterErrorKind::IOError, error:Some(Box::new(err))}),
};
let parsed_content = match parse_yaml(content) {
Ok(parsed) => parsed,
Err(err) => return Err(AdapterError {kind: AdapterErrorKind::ParseError, error:Some(Box::new(err))}),
};
let software_name = match &dirname.path().to_str() {
Some(val) => split_path(val),
let version_name = match &vdirname.path().to_str() {
Some(val) => split_path(val),
None => String::from(""),
};
let job_name = match &filename.path().to_str() {
Some(val) => {
let splited = split_path(val);
splited.replace(".yaml", "")
},
None => String::from(""),
};
if let hash_map::Entry::Vacant(e) = parsed_configurations.entry(software_name.clone()) {
let mut job_map = HashMap::new();
job_map.insert(job_name, result.clone());
let mut version_map = HashMap::new();
version_map.insert(version_name, job_map);
e.insert(version_map);
let version_map = parsed_configurations.get_mut(&software_name.to_string()).unwrap();
if version_map.contains_key(&version_name.to_string()) {
let job_map = version_map.get_mut(&version_name.to_string()).unwrap();
job_map.insert(job_name, result.clone());
let mut job_map = HashMap::new();
job_map.insert(job_name, result.clone());
version_map.insert(version_name, job_map);
},
Err(err) => return Err(AdapterError {kind: AdapterErrorKind::ParseError, error:Some(Box::new(err))}),
};
}
}
}
pub fn get_conf_by_uuid(id: Uuid, configurations: Configurations) -> Option<JobConfCommand> {
for (_, version_map) in configurations.into_iter() {
for (_, job_map) in version_map.into_iter() {
for (_, conf) in job_map.into_iter() {
if conf.uuid == id {
return Some(conf);
}
}
}
}
pub fn generate_command(cmd_params: HashMap<String, String>, conf: JobConfCommand) -> Result<String, CommandError> {
cmd.push_str(&(conf.bin));
for param in &conf.parameters {
if param.required && !cmd_params.contains_key(¶m.name) {
return Err(CommandError::MissingRequiredParam);
} else if let Some(val) = cmd_params.get(¶m.name) {
if param.named {
cmd.push_str(format!(" {} {}", ¶m.parameter_name.clone().unwrap(), &val).as_str());
} else {
cmd.push_str(format!(" {}", &val).as_str());
}
#[cfg(not(tarpaulin_include))]
pub fn generate_sbatch(job_params: HashMap<String, String>, cmd: String) -> Result<String, tera::Error>{
let tera = match Tera::new("template/*") {
Err(err) => return Err(err)
};
let mut context = Context::new();
context.insert(String::from("command"), &cmd.as_str());
let content = match tera.render("template.sbatch", &context) {
Ok(result) => result,
Err(err) => {
return Err(err)
}
#[cfg(not(tarpaulin_include))]
pub fn run_sbatch(sbatch_path: &Path) -> Result<String, IOError> {
let job_id = match Command::new("sbatch").arg(sbatch_path).output() {
let stdout_result = String::from_utf8_lossy(&result.stdout);
let split = stdout_result.split(' ');
let split_vec = split.collect::<Vec<&str>>();
let job_id = split_vec[split_vec.len()-1];
},
Err(err) => return Err(err)
};
#[cfg(not(tarpaulin_include))]
pub fn get_slurm_job_infos(slurm_job_id: String) -> Result<SlurmJobInfos, IOError> {
let job_infos = match Command::new("scontrol").arg("show").arg("job").arg(slurm_job_id).output() {
Ok(result) => {
let stdout_result = String::from_utf8_lossy(&result.stdout);
let split = stdout_result.split(' ');
let split_vec = split.collect::<Vec<&str>>();
let keys = &["JobState", "RunTime", "SubmitTime", "StartTime", "EndTime"];
let trim_split_vec = split_vec.clone().into_iter().filter(|e| e.to_string().ne("")).filter(|e| keys.iter().any(|k| k.eq(e))).collect::<Vec<&str>>();
SlurmJobInfos {
run_time: String::from(trim_split_vec[1].split('=').collect::<Vec<&str>>()[1]),
submit_time: String::from(trim_split_vec[2].split('=').collect::<Vec<&str>>()[1]),
start_time: String::from(trim_split_vec[3].split('=').collect::<Vec<&str>>()[1]),
end_time: String::from(trim_split_vec[4].split('=').collect::<Vec<&str>>()[1]),
status: String::from(trim_split_vec[0].split('=').collect::<Vec<&str>>()[1])
}
},
Err(err) => return Err(err)
};
Ok(job_infos)
}
#[cfg(test)]
mod tests {
use super::get_conf_by_uuid;
use crate::constants::*;
#[test]
fn test_correct_conf() {
uuid: Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1d").unwrap(),

Francesco WITZ
committed
bin: String::from("echo"),
parameters: vec![JobConfCommandParam {
name: String::from("to_print"),
value_type: JobConfValueType::String,
default_value: Some(String::from("")),
comment: Some(String::from("Value to print on the cli")),
required: true,
named: false,
parameter_name: None
}]
};
let conf = check_syntax(&conf_expected);
assert_eq!(conf.unwrap(), &conf_expected);
}
#[test]
fn test_missing_name() {
let conf_to_test = JobConfCommand {
uuid: Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1d").unwrap(),
bin: String::from("echo"),
parameters: vec![JobConfCommandParam {
name: String::from("to_print"),
value_type: JobConfValueType::String,
default_value: Some(String::from("")),
comment: Some(String::from("Value to print on the cli")),
required: true,
named: true,
parameter_name: None
}]
};
assert_eq!(conf.unwrap_err(), JobConfError::MissingValue);
fn test_wrong_integer_cast() {
let conf_to_test = JobConfCommand {
uuid: Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1d").unwrap(),
bin: String::from("echo"),
parameters: vec![JobConfCommandParam {
name: String::from("to_print"),
value_type: JobConfValueType::Integer,
default_value: Some(String::from("test")),
comment: Some(String::from("Value to print on the cli")),
required: true,
named: true,
parameter_name: None
}]
};
assert_eq!(conf.unwrap_err(), JobConfError::ParseIntError);
#[test]
fn test_wrong_boolean_cast() {
let conf_to_test = JobConfCommand {
uuid: Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1d").unwrap(),
bin: String::from("echo"),
parameters: vec![JobConfCommandParam {
name: String::from("to_print"),
value_type: JobConfValueType::Boolean,
default_value: Some(String::from("test")),
comment: Some(String::from("Value to print on the cli")),
required: true,
named: true,
parameter_name: None
}]
};
let conf = check_syntax(&conf_to_test);
assert_eq!(conf.unwrap_err(), JobConfError::ParseBoolError);
}
#[test]
fn test_wrong_float_cast() {
let conf_to_test = JobConfCommand {
uuid: Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1d").unwrap(),
bin: String::from("echo"),
parameters: vec![JobConfCommandParam {
name: String::from("to_print"),
value_type: JobConfValueType::Float,
default_value: Some(String::from("test")),
comment: Some(String::from("Value to print on the cli")),
required: true,
named: true,
parameter_name: None
}]
};
let conf = check_syntax(&conf_to_test);
assert_eq!(conf.unwrap_err(), JobConfError::ParseFloatError);
}
#[test]
fn test_missing_required_param() {
let conf = JobConfCommand {
uuid: Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1d").unwrap(),
bin: String::from("echo"),
parameters: vec![JobConfCommandParam {
name: String::from("to_print"),
value_type: JobConfValueType::String,
default_value: Some(String::from("")),
comment: Some(String::from("Value to print on the cli")),
required: true,
named: false,
parameter_name: None
}]
};
let cmd = generate_command(cmd_params, conf);
assert_eq!(cmd.unwrap_err(), CommandError::MissingRequiredParam);
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
#[test]
fn test_correct_conf_retrieving() {
let conf = JobConfCommand {
uuid: Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1d").unwrap(),
bin: String::from("echo"),
parameters: vec![JobConfCommandParam {
name: String::from("to_print"),
value_type: JobConfValueType::String,
default_value: Some(String::from("")),
comment: Some(String::from("Value to print on the cli")),
required: true,
named: false,
parameter_name: None
}]
};
let mut jobs: HashMap<String, JobConfCommand> = HashMap::new();
jobs.insert("test".to_string(), conf.clone());
let mut versions: HashMap<String, HashMap<String, JobConfCommand>> = HashMap::new();
versions.insert("v0.4.0".to_string(), jobs);
let mut configurations: Configurations = HashMap::new();
configurations.insert("cryosparc".to_string(), versions);
let found_conf = get_conf_by_uuid(Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1d").unwrap(), configurations);
assert_eq!(found_conf, Some(conf));
}
#[test]
fn test_conf_not_found() {
let conf = JobConfCommand {
uuid: Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1d").unwrap(),
bin: String::from("echo"),
parameters: vec![JobConfCommandParam {
name: String::from("to_print"),
value_type: JobConfValueType::String,
default_value: Some(String::from("")),
comment: Some(String::from("Value to print on the cli")),
required: true,
named: false,
parameter_name: None
}]
};
let mut jobs: HashMap<String, JobConfCommand> = HashMap::new();
jobs.insert("test".to_string(), conf.clone());
let mut versions: HashMap<String, HashMap<String, JobConfCommand>> = HashMap::new();
versions.insert("v0.4.0".to_string(), jobs);
let mut configurations: Configurations = HashMap::new();
configurations.insert("cryosparc".to_string(), versions);
let found_conf = get_conf_by_uuid(Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1e").unwrap(), configurations);
assert_eq!(found_conf, None);
}
#[test]
fn test_correct_command_generation() {
let conf = JobConfCommand {
uuid: Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1d").unwrap(),
bin: String::from("echo"),
parameters: vec![JobConfCommandParam {
name: String::from("to_print"),
value_type: JobConfValueType::String,
default_value: Some(String::from("")),
comment: Some(String::from("Value to print on the cli")),
required: true,
named: false,
parameter_name: None
}]
};
let mut cmd_params = HashMap::new();
cmd_params.insert("to_print".to_string(), "Hello World !".to_string());
let cmd = generate_command(cmd_params, conf);
assert_eq!(cmd.unwrap(), String::from("echo Hello World !"));
#[test]
fn test_correct_named_command_generation() {
let conf = JobConfCommand {
uuid: Uuid::parse_str("c5e9d770-73ea-43f5-84bb-cd612c0c7b1d").unwrap(),
bin: String::from("echo"),
parameters: vec![JobConfCommandParam {
name: String::from("to_print"),
value_type: JobConfValueType::String,
default_value: Some(String::from("")),
comment: Some(String::from("Value to print on the cli")),
required: true,
named: true,
parameter_name: Some(String::from("-n"))
}]
};
let mut cmd_params = HashMap::new();
cmd_params.insert("to_print".to_string(), "Hello World !".to_string());
let cmd = generate_command(cmd_params, conf);
assert_eq!(cmd.unwrap(), String::from("echo -n Hello World !"));
}