use clap::{Arg, Command}; pub mod mvn_tm_temp { use std::fs::File; use std::io::{BufRead, BufReader}; use chrono::{NaiveDateTime}; use lazy_static::lazy_static; use regex::Regex; use walkdir::WalkDir; #[derive(Debug)] struct TMtemp { // idx: String, val: f32, timestamp_s: i64, date: String, time: String, } impl TMtemp { pub fn new(val: f32, timestamp_s: i64, date: String, time: String) -> TMtemp { TMtemp { val, timestamp_s, date, time } } } lazy_static! { pub static ref dtime_fmt: String = String::from("%d.%m.%Y %H:%M:%S%.f"); pub static ref pattern_fname: String = String::from("MVN_.*_A-.*.txt"); pub static ref err_patterns: Vec = { let mut err = Vec::new(); err.push("Error parsing file".to_string()); err }; pub static ref tm_idx: Vec = { let mut tm = Vec::new(); tm.push("T1MBH".to_string()); tm.push("T2MBH".to_string()); tm.push("T3MBH".to_string()); tm.push("T4MBH".to_string()); tm }; } pub fn parse_data_file(filename_full: &str, save_data: bool, disp_data: bool, silent_mode: bool) -> Result<(), String> { let filename = match filename_full.rfind('/') { Some(idx) => filename_full[idx+1..filename_full.len()].to_string(), _ => filename_full.to_string(), }; let mut tm_data_v: [Vec; 4] = [Vec::new(), Vec::new(), Vec::new(), Vec::new()]; read_data(filename_full, &mut tm_data_v)?; sort_data(&mut tm_data_v); if save_data == true { for (i, &ref elem) in tm_data_v.iter().enumerate() { let fname = filename.replace(".txt", &format!("_{}{}", tm_idx[i], ".csv")); save_data_csv(&elem, &fname, disp_data)?; if silent_mode == false { println!("save csv data to file {}", fname); } } } return Ok(()); } pub fn parse_data_dir(dir: &str, save_data: bool, disp_data: bool, silent_mode: bool) -> Result<(), String> { let mut tm_data_v: [Vec; 4] = [Vec::new(), Vec::new(), Vec::new(), Vec::new()]; if silent_mode == false { println!("parsing data from directory: {} ...", dir); } let files = find_files_regex(dir, &pattern_fname)?; for file in files { read_data(&file, &mut tm_data_v)?; } sort_data(&mut tm_data_v); if save_data == true { for (i, &ref elem) in tm_data_v.iter().enumerate() { let fname = format!("MVN_data_{}{}", tm_idx[i], ".csv"); save_data_csv(&elem, &fname, disp_data)?; if silent_mode == false { println!("save csv data to file {}", fname); } } } return Ok(()); } fn find_files_regex(dir: &str, template: &str) -> Result, String> { let mut path_vec: Vec = Vec::new(); let regex = match Regex::new(template) { Ok(val) => val, Err(msg) => { return Err(format!("Error create regex template ({}): {}", template, msg)); } }; let data = WalkDir::new(dir) .into_iter() .filter_map(|e| e.ok()) .filter(|e| regex.is_match(e.file_name().to_str().unwrap())) .map(|e| e.into_path()); for elem in data { path_vec.push(elem.display().to_string()); } if path_vec.len() == 0 { return Err(format!( "Error searching files for pattern ({}): files not found in directory {}", template, dir )); } return Ok(path_vec); } fn read_data(filename: &str, data: &mut [Vec; 4]) -> Result<(), String> { let file = match File::open(filename) { Ok(_file) => _file, Err(msg) => { return Err(format!("Error opening data file {}: {}", filename, msg)) } }; let reader_buf = BufReader::new(file); let data_buf: Vec = match reader_buf.lines() .map(|line| line) .collect() { Ok(_line) => _line, Err(msg) => { return Err(format!("Error reading file {}: {}", filename, msg)) }, }; let mut val: Vec = Vec::new(); for (j, elem) in data_buf.iter().enumerate() { for (i, idx) in tm_idx.clone().into_iter().enumerate() { if elem.contains(&idx) { val = match elem.split_whitespace() .map(|elem| elem.parse::()) .collect() { Ok(_line) => _line, Err(msg) => { return Err(format!("{} {} (string: {}): {}", err_patterns[0], filename, j, msg)) }, }; let dtime_str = format!("{} {}", val[4].clone(), val[5].clone()); let naive_dtime = match NaiveDateTime::parse_from_str(&dtime_str, &dtime_fmt) { Ok(_dtime) => _dtime, Err(msg) => { return Err(format!("{} {} (string: {}). Can not decode datetime: {}", err_patterns[0], filename, j, msg)) }, }; let timestamp = naive_dtime.and_utc().timestamp(); let temperature = match val[1].parse::() { Ok(_val) => _val, Err(msg) => { return Err(format!("{} {} (string: {}). Temperature string can not convert to float: {}", err_patterns[0], filename, j, msg)) }, }; let tm_data = TMtemp::new(temperature, timestamp, val[4].clone(), val[5].clone()); data[i].push(tm_data); } } } return Ok(()); } fn sort_data(data: &mut [Vec; 4]) { for elem in data { elem.sort_by(|a, b| a.timestamp_s.cmp(&b.timestamp_s)) } } fn save_data_csv(data: &Vec, fname: &str, disp_flag: bool) -> Result<(), String> { let mut data_v: Vec = Vec::new(); for elem in data { let s = format!("{};{};{}", elem.date, elem.time, elem.val); if disp_flag == true { println!("{}", s); } data_v.push(s); } match std::fs::write(fname, data_v.join("\n")) { Ok(f) => f, Err(msg) => { return Err(format!("Error writing data to csv file {}: {}", fname, msg)) } } return Ok(()); } } fn main() { use crate::mvn_tm_temp::*; let arguments = Command::new("mvn_accomp_csv") .version("0.1.0") .author("Danila Gamkov ") .about("raw accompanying MVN data parser") .arg( Arg::new("file") .short('f') .long("filename") .help(format!("file with accompanying MVN data ({})", pattern_fname.to_string())) ) .arg( Arg::new("dir") .short('d') .long("directory") .help(format!("directory with accompanying MVN data")) ) .get_matches(); match arguments.get_one::("file") { Some(fname) => { match parse_data_file(&fname.clone(), true, true, false) { Ok(elem) => elem, Err(msg) => { println!("{}", msg); return; } } return; }, _ => { } }; match arguments.get_one::("dir") { Some(path) => { match parse_data_dir(path, true, true, false) { Ok(_) => {} Err(msg) => { println!("{}", msg); return; } } return; }, _ => { } } println!("Unexpected command. Type --help for more iformation"); }