toddi 0.2.0

A TODO focuser built on top of todo.txt
Documentation
use std::{path::PathBuf, process::Command};

use assert_cmd::{assert::OutputAssertExt, cargo::CommandCargoExt};
use assert_fs::prelude::FileWriteStr;
use predicates::prelude::predicate;

#[cfg(test)]
mod tests {
    use super::*;

    const DIR: &str = env!("CARGO_MANIFEST_DIR");

    fn get_config_path_str() -> String {
        let mut path_buf = PathBuf::from(DIR);
        path_buf.push("tests/config.toml");
        path_buf.to_str().unwrap().to_string()
    }

    #[test]
    fn todo_txt_file_does_not_exist() -> Result<(), Box<dyn std::error::Error>> {
        let mut cmd = Command::cargo_bin("toddi")?;

        cmd.arg("-t")
            .arg("test/file/does/not/exist")
            .arg("-c")
            .arg(get_config_path_str());
        cmd.assert()
            .failure()
            .stderr(predicate::str::contains("could not read file"));

        Ok(())
    }

    #[test]
    fn task_exists() -> Result<(), Box<dyn std::error::Error>> {
        let todo = assert_fs::NamedTempFile::new("todo.txt")?;
        let done = assert_fs::NamedTempFile::new("done.txt")?;
        todo.write_str(
            "(B) match this +alpha\nDo not match\nDo not match\n(C) Do not match +alpha",
        )?;
        done.write_str("(A) Do not match +alpha\n(A) Do not match +bravo")?;

        let mut cmd = Command::cargo_bin("toddi")?;
        cmd.arg("-t")
            .arg(todo.path())
            .arg("-d")
            .arg(done.path())
            .arg("-c")
            .arg(get_config_path_str())
            .arg("focus")
            .arg("alpha");
        cmd.assert()
            .success()
            .stdout(predicate::str::contains("Project ALPHA"))
            .stdout(predicate::str::contains("todo: <<< match this >>>"));

        Ok(())
    }

    #[test]
    fn tasks_exist() -> Result<(), Box<dyn std::error::Error>> {
        let todo = assert_fs::NamedTempFile::new("todo.txt")?;
        let done = assert_fs::NamedTempFile::new("done.txt")?;
        todo.write_str(
            "(B) match me +alpha\nmatch this as well +bravo\nDo not match\n(C) Do not match +alpha",
        )?;
        done.write_str("(A) Do not match +alpha\n(A) Do not match +bravo")?;

        let mut cmd = Command::cargo_bin("toddi")?;
        cmd.arg("-t")
            .arg(todo.path())
            .arg("-d")
            .arg(done.path())
            .arg("-c")
            .arg(get_config_path_str())
            .arg("focus")
            .arg("alpha")
            .arg("bravo");
        cmd.assert()
            .success()
            .stdout(predicate::str::contains("Project ALPHA"))
            .stdout(predicate::str::contains("todo: <<< match me >>>"))
            .stdout(predicate::str::contains("Project BRAVO"))
            .stdout(predicate::str::contains("todo: <<< match this as well >>>"));

        Ok(())
    }

    #[test]
    fn task_not_found() -> Result<(), Box<dyn std::error::Error>> {
        let todo = assert_fs::NamedTempFile::new("todo.txt")?;
        let done = assert_fs::NamedTempFile::new("done.txt")?;
        todo.write_str(
            "(A) Do not match +alpha\nDo not match\nDo not match\n(B) Do not match +alpha",
        )?;
        done.write_str("")?;

        let mut cmd = Command::cargo_bin("toddi")?;
        cmd.arg("-t")
            .arg(todo.path())
            .arg("-d")
            .arg(done.path())
            .arg("-c")
            .arg(get_config_path_str())
            .arg("focus")
            .arg("bravo");
        cmd.assert().success().stdout(predicate::str::contains(
            "Project `bravo` contains no task as of now",
        ));

        Ok(())
    }

    #[test]
    fn no_project_name_input() -> Result<(), Box<dyn std::error::Error>> {
        let todo = assert_fs::NamedTempFile::new("todo.txt")?;
        let done = assert_fs::NamedTempFile::new("done.txt")?;
        todo.write_str(
            "(A) match this +alpha\nDo not match\nDo not match\n(B) Do not match +alpha",
        )?;
        done.write_str("")?;

        let mut cmd = Command::cargo_bin("toddi")?;
        cmd.arg("-t")
            .arg(todo.path())
            .arg("-c")
            .arg(get_config_path_str())
            .arg("-d")
            .arg(done.path());
        cmd.assert()
            .success()
            .stdout(predicate::str::contains("Project ALPHA"))
            .stdout(predicate::str::contains("todo: <<< match this >>>"));

        Ok(())
    }

    #[test]
    fn empty_todo_list() -> Result<(), Box<dyn std::error::Error>> {
        let todo = assert_fs::NamedTempFile::new("todo.txt")?;
        let done = assert_fs::NamedTempFile::new("done.txt")?;
        todo.write_str("")?;
        done.write_str("")?;

        let mut cmd = Command::cargo_bin("toddi")?;
        cmd.arg("-t")
            .arg(todo.path())
            .arg("-c")
            .arg(get_config_path_str())
            .arg("-d")
            .arg(done.path());
        cmd.assert()
            .success()
            .stdout(predicate::str::contains("todo list is empty!"));

        Ok(())
    }

    #[test]
    fn no_partial_project_name_match() -> Result<(), Box<dyn std::error::Error>> {
        let todo = assert_fs::NamedTempFile::new("todo.txt")?;
        let done = assert_fs::NamedTempFile::new("done.txt")?;
        todo.write_str(
            "(A) Do not match this +alpha\nDo not match\nDo not match\n(B) Do not match +alpha",
        )?;
        done.write_str("")?;

        let mut cmd = Command::cargo_bin("toddi")?;
        cmd.arg("-t")
            .arg(todo.path())
            .arg("-d")
            .arg(done.path())
            .arg("-c")
            .arg(get_config_path_str())
            .arg("focus")
            .arg("alph");
        cmd.assert().success().stdout(predicate::str::contains(
            "Project `alph` contains no task as of now.",
        ));

        Ok(())
    }

    #[test]
    fn test_configuration_input() -> Result<(), Box<dyn std::error::Error>> {
        let todo = assert_fs::NamedTempFile::new("todo.txt")?;
        let done = assert_fs::NamedTempFile::new("done.txt")?;
        let config = assert_fs::NamedTempFile::new("config.toml")?;
        todo.write_str(
            "(A) Match this task +alpha\nDo not match\nDo not match\n(B) Do not match +alpha",
        )?;
        done.write_str("")?;
        let path_buf = todo.to_path_buf();
        let todo_path_string = path_buf.to_str().unwrap();
        let path_buf = done.to_path_buf();
        let done_path_string = path_buf.to_str().unwrap();
        let mut config_str: String = String::from("todo_file = '");
        config_str.push_str(todo_path_string);
        config_str.push_str("'\ndone_file = '");
        config_str.push_str(done_path_string);
        config_str.push('\'');
        config.write_str(&config_str)?;

        let mut cmd = Command::cargo_bin("toddi")?;
        cmd.arg("-c").arg(config.path()).arg("focus").arg("alpha");
        cmd.assert()
            .success()
            .stdout(predicate::str::contains("Match this task"));

        Ok(())
    }
}