diff --git a/README.md b/README.md index 5066805..d42f2af 100644 --- a/README.md +++ b/README.md @@ -264,4 +264,12 @@ This is especially useful for autocompletion. For example, adding this line to t ``` complete -W "$(bartib projects)" bartib +``` + +#### Check bartib file + +Checks the bartib file for lines that it can not parse as activities: + +``` +bartib check ``` \ No newline at end of file diff --git a/src/controller/list.rs b/src/controller/list.rs index 6a4ce9a..a80db79 100644 --- a/src/controller/list.rs +++ b/src/controller/list.rs @@ -42,6 +42,32 @@ pub fn list(file_name: &str, filter: getter::ActivityFilter, do_group_activities Ok(()) } +// prints all errors that occured when reading the bartib file +pub fn check(file_name: &str) -> Result<()> { + let file_content = bartib_file::get_file_content(file_name)?; + + let number_of_errors = file_content.iter() + .filter(|line| line.activity.is_err()) + .count(); + + if number_of_errors == 0 { + println!("All lines in the file have been successfully parsed as activities."); + return Ok(()); + } + + println!("Found {} line(s) with parsing errors", number_of_errors); + + file_content.iter() + .filter(|line| line.activity.is_err() && line.plaintext.is_some()) + .for_each(|line| { + if let Err(e) = &line.activity { + println!("\n{}\n -> {} (Line: {})", line.plaintext.as_ref().unwrap(), e.to_string(), line.line_number.unwrap_or(0)); + } + }); + + Ok(()) +} + // lists all projects pub fn list_projects(file_name: &str) -> Result<()> { let file_content = bartib_file::get_file_content(file_name)?; diff --git a/src/data/bartib_file.rs b/src/data/bartib_file.rs index 6f8b503..7071955 100644 --- a/src/data/bartib_file.rs +++ b/src/data/bartib_file.rs @@ -17,7 +17,9 @@ pub enum LineStatus { pub struct Line { // the plaintext of the line as it has been read from the file // we save this to be able write untouched lines back to file without chaning them - plaintext: String, + pub plaintext: Option, + // the line number + pub line_number: Option, // the result of parsing this line to a activity pub activity: Result, // the status of this activity @@ -26,9 +28,10 @@ pub struct Line { impl Line { // creates a new line struct from plaintext - pub fn new(plaintext: &str) -> Line { + pub fn new(plaintext: &str, line_number: usize) -> Line { Line { - plaintext: plaintext.trim().to_string(), + plaintext: Some(plaintext.trim().to_string()), + line_number: Some(line_number), activity: activity::Activity::from_str(plaintext), status: LineStatus::Unchanged, } @@ -37,7 +40,8 @@ impl Line { // creates a new line from an existing activity pub fn for_activity(activity: activity::Activity) -> Line { Line { - plaintext: "".to_string(), + plaintext: None, + line_number: None, activity: Ok(activity), status: LineStatus::Changed, } @@ -58,7 +62,8 @@ pub fn get_file_content(file_name: &str) -> Result> { let lines = reader .lines() .filter_map(|line_result| line_result.ok()) - .map(|line| Line::new(&line)) + .enumerate() + .map(|(line_number, line)| Line::new(&line, line_number.saturating_add(1))) .collect(); Ok(lines) @@ -69,8 +74,14 @@ pub fn write_to_file(file_name: &str, file_content: &[Line]) -> Result<(), io::E let file_handler = get_bartib_file_writable(file_name)?; for line in file_content { - match line.status { - LineStatus::Unchanged => writeln!(&file_handler, "{}", line.plaintext)?, + match &line.status { + LineStatus::Unchanged => { + if let Some(plaintext) = &line.plaintext { + writeln!(&file_handler, "{}", plaintext)? + } else { + write!(&file_handler, "{}", line.activity.as_ref().unwrap())? + } + }, LineStatus::Changed => write!(&file_handler, "{}", line.activity.as_ref().unwrap())?, } } diff --git a/src/main.rs b/src/main.rs index fca3d5e..bcffd2d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -192,6 +192,7 @@ fn main() -> Result<()> { .takes_value(true), ), ) + .subcommand(SubCommand::with_name("check").about("checks file and reports parsing errors")) .get_matches(); let file_name = matches.value_of("file") @@ -284,6 +285,7 @@ fn run_subcommand(matches: &ArgMatches, file_name: &str) -> Result<()> { let optional_editor_command = sub_m.value_of("editor"); bartib::controller::manipulation::start_editor(file_name, optional_editor_command) } + ("check", Some(_)) => bartib::controller::list::check(file_name), _ => bail!("Unknown command"), } }