diff --git a/.gitignore b/.gitignore index 70bf74b..227504f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ Cargo.lock **/*.rs.bk # End of https://www.gitignore.io/api/rust + +fs-scan_output.csv \ No newline at end of file diff --git a/src/csv.rs b/src/csv.rs new file mode 100644 index 0000000..6971553 --- /dev/null +++ b/src/csv.rs @@ -0,0 +1,85 @@ +use std::fs::OpenOptions; +use std::io::BufRead; +use std::io::BufReader; +use std::io::Write; + +use super::objects; + +static OUTPUT_FILE: &'static str = "fs-scan_output.csv"; +static FILE_FIRST_LINE: &'static str = "Path,Files,Directories,4K,4K_16K,16K_64K,64K_128K,128K_256K,256K_512K,512K_1M,1M_10M,10M_100M,100M_1G,1G"; + +pub fn save(res: &objects::Result) { + match check_file() { + Err(s) => { + println!("ERROR on check: {}", s); + return; + } + Ok(s) => println!("SUCCESS on check: {}", s), + } + + let mut file = OpenOptions::new() + .write(true) + .append(true) + .open(&OUTPUT_FILE) + .unwrap(); + + if let Err(_) = writeln!(file, "{}", res.result_to_string()) { + return; + } +} + +fn check_file() -> Result { + // Open the file + let mut file = match OpenOptions::new() + .read(true) + .write(true) + .open(&OUTPUT_FILE) + { + Err(_) => { + // File not opened + // Try to create it + let file = match OpenOptions::new() + .write(true) + .read(true) + .append(true) + .create(true) + .open(&OUTPUT_FILE) + { + Err(_) => return Err("can't open/create new file".to_string()), + Ok(file) => file, + }; + file + } + Ok(file) => file, + }; + + match file.metadata() { + Ok(m) => { + if m.len() == 0 { + if let Err(_) = writeln!(&mut file, "{}", &String::from(FILE_FIRST_LINE)) { + return Err("can't write first line".to_string()); + } + return Ok("new file created and first line added successfully".to_string()); + } + } + Err(_) => return Err("Can't get meta".to_string()), + } + + // Check the first line is valid + // + // Read the content + let file_content = BufReader::new(&file); + for line in file_content.lines() { + // let l = line.unwrap(); + let l = match line { + Ok(l) => l, + Err(e) => return Err(format!("can't read line: {}", e)), + }; + if l == FILE_FIRST_LINE { + break; + } + return Err(format!("Not the same line: content {}", l)); + } + + Ok("first line valid, can add the new report".to_string()) +} diff --git a/src/main.rs b/src/main.rs index 8e0d4f0..b11805f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,17 @@ +mod csv; mod objects; use std::env; +use std::fs; use std::path::PathBuf; use std::sync::mpsc::{channel, Sender}; use std::thread; use std::time; -use std::{fs, io}; use indicatif::{HumanDuration, ProgressBar, ProgressStyle}; use num_cpus; - - -fn main() -> io::Result<()> { +fn main() { let mut path = PathBuf::from("."); let args: Vec<_> = env::args().collect(); if args.len() > 1 { @@ -20,7 +19,11 @@ fn main() -> io::Result<()> { path = PathBuf::from(&args[1]); }; - let mut res = objects::build_result(); + let mut res; + match &path.to_str() { + Some(path_as_string) => res = objects::build_result(path_as_string), + None => res = objects::build_result(""), + } // build channel let (sender, receiver) = channel(); @@ -43,7 +46,9 @@ fn main() -> io::Result<()> { let starting_point = time::Instant::now(); let display_refresh_time = time::Duration::from_millis(250); - let mut last_message = time::Instant::now().checked_sub(display_refresh_time.clone()).expect("to remove some time"); + let mut last_message = time::Instant::now() + .checked_sub(display_refresh_time.clone()) + .expect("to remove some time"); // Handle responses for received in receiver { @@ -89,7 +94,6 @@ fn main() -> io::Result<()> { } match dir_queue.pop() { Some(dir) => { - // // Add latency to debug the display // thread::sleep(time::Duration::from_millis(5)); @@ -108,6 +112,8 @@ fn main() -> io::Result<()> { } bar.finish(); + csv::save(&res); + println!("Scan took {}", HumanDuration(starting_point.elapsed())); println!("Files -> {}", nice_number(res.files)); println!("Directories -> {}", nice_number(res.directories)); @@ -157,8 +163,6 @@ fn main() -> io::Result<()> { nice_number(res.between_100_m_1_g) ); println!("More than 1GB -> {}", nice_number(res.more_than_1_g)); - - Ok(()) } fn nice_number(input: usize) -> String { diff --git a/src/objects.rs b/src/objects.rs index e7e0d1b..e3aa034 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; pub struct Result { + pub path: String, pub files: usize, pub directories: usize, pub less_than_4_k: usize, @@ -17,8 +18,10 @@ pub struct Result { pub between_100_m_1_g: usize, pub more_than_1_g: usize, } -pub fn build_result() -> Result { +pub fn build_result(path: &str) -> Result { Result { + path: String::from(path), + files: 0, directories: 0, @@ -38,6 +41,29 @@ pub fn build_result() -> Result { } } +impl Result { + pub fn result_to_string(&self) -> String { + format!( + "{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}", + &self.path, + &self.files, + &self.directories, + &self.less_than_4_k, + &self.between_4_k_8_k, + &self.between_8_k_16_k, + &self.between_16_k_32_k, + &self.between_32_k_64_k, + &self.between_64_k_128_k, + &self.between_128_k_256_k, + &self.between_256_k_512_k, + &self.between_512_k_1_m, + &self.between_1_m_10_m, + &self.between_10_m_100_m, + &self.between_100_m_1_g, + &self.more_than_1_g, + ) + } +} pub enum ResponseType { File, @@ -69,4 +95,4 @@ pub fn build_file_chan(size: u64) -> ChanResponse { path: PathBuf::new(), len: size, } -} \ No newline at end of file +}