mj 0.4.3

My Journal - personal tool to capture ideas, work with journals, notes and tasks in your favourite text $EDITOR.
Documentation
use crate::core::*;
use std::collections::HashMap;
use std::fs;
use std::io::Write;
use std::path::Path;

pub fn edit(vault: &Vault, project: &String) -> Result<bool> {
  let base = Path::new(&vault.tasks).join(project);
  fs::create_dir_all(&base)?;
  let path = base.join("tasks").with_extension("md");
  let content_before = read_safe(&path);
  editor::run(&path)?;
  let content_after = read_safe(&path);
  let saved = content_before != content_after;
  // cleanup if necessary
  if content_after == "" && path.exists() {
    fs::remove_file(&path)?;
  }
  if content_after == "" && base.read_dir()?.count() == 0 {
    fs::remove_dir_all(&base)?;
  }
  Ok(saved)
}

pub fn add_task(vault: &Vault, project: &String, task: &String) -> Result<()> {
  let base = Path::new(&vault.tasks).join(project);
  fs::create_dir_all(&base)?;
  let path = base.join("tasks").with_extension("md");
  if !path.exists() {
    std::fs::File::create(&path)?;
  }
  let mut file = std::fs::OpenOptions::new().append(true).open(&path)?;
  writeln!(file, "{} {}", vault.config.task_not_done_prefix, task)?;
  Ok(())
}

pub fn rotate_state(vault: &Vault, project: &String, task: &String) -> Result<String> {
  let base = Path::new(&vault.tasks).join(project);
  let path = base.join("tasks").with_extension("md");
  let mut ring: HashMap<String, String> = HashMap::new();
  let not_done = vault.config.task_not_done_prefix.as_str();
  let in_progress = vault.config.task_in_progress_prefix.as_str();
  let done = vault.config.task_done_prefix.as_str();
  ring.insert(
    not_done.to_owned(),
    in_progress.to_owned(),
  );
  ring.insert(
    in_progress.to_owned(),
    done.to_owned(),
  );
  ring.insert(
    done.to_owned(),
    not_done.to_owned(),
  );
  if path.exists() {
    let content = fs::read_to_string(&path)?;
    let t = task.as_str();
    let new_task =
      if t.starts_with(not_done) {
        let new_prefix = ring.get(&not_done.to_owned()).unwrap();
        let value = t.trim_start_matches(not_done).trim_start();
        format!("{} {}", new_prefix, value)
      } else if t.starts_with(in_progress) {
        let new_prefix = ring.get(&in_progress.to_owned()).unwrap();
        let value = t.trim_start_matches(in_progress).trim_start();
        format!("{} {}", new_prefix, value)
      } else if t.starts_with(done) {
        let new_prefix = ring.get(&done.to_owned()).unwrap();
        let value = t.trim_start_matches(done).trim_start();
        format!("{} {}", new_prefix, value)
      } else {
        t.to_owned()
      };
    let new_content = content.as_str().replace(t, new_task.as_str());
    std::fs::File::create(&path)?;
    let mut file = std::fs::OpenOptions::new().append(true).open(&path)?;
    write!(file, "{}", new_content)?;
    return Ok(new_task);
  }
  Ok(task.to_owned())
}

pub fn remove(vault: &Vault, project: &String) -> Result<()> {
  let base = Path::new(&vault.tasks).join(project);
  let path = base.join("tasks").with_extension("md");
  if path.exists() {
    let question = format!("Are you sure you want to remove tasks for [{}]?", project);
    match prompt_yes_no(question.as_str(), YesNo::No)? {
      YesNo::Yes => {
        fs::remove_file(&path)?;
        if base.read_dir()?.count() == 0 {
          fs::remove_dir_all(&base)?;
        }
        println!("Tasks for [{}] was successfully removed", project);
      }
      YesNo::No => println!("Nothing was removed"),
    }
  } else {
    println!("Tasks for [{}] was not found", project);
  }
  Ok(())
}

pub fn remove_noprompt(vault: &Vault, project: &String, task: &String) -> Result<bool> {
  let base = Path::new(&vault.tasks).join(project);
  let path = base.join("tasks").with_extension("md");
  if path.exists() {
    let content = fs::read_to_string(&path)?;
    let lines = content.as_str().lines();
    let new_lines =
      lines.into_iter()
        .filter(|x| x.trim_start() != task.as_str())
        .map(|x| x.to_owned())
        .collect::<Vec<String>>();
    if new_lines.len() == 0 {
      fs::remove_file(&path)?;
      if base.read_dir()?.count() == 0 {
        fs::remove_dir_all(&base)?;
      }
    } else {
      let mut new_content = String::from("");
      for line in new_lines {
        let line = format!("{}\n", line);
        new_content.push_str(line.as_str());
        fs::write(&path, &new_content)?;
      }
    }
    Ok(true)
  } else {
    Ok(false)
  }
}

pub fn list(vault: &Vault, show_tasks: bool, project: Option<String>) -> Result<()> {
  let show_project = project.is_none();
  for (project, tasks) in vault.list_tasks(show_tasks, &project)? {
    if show_project {
      println!("{}", project);
      for note in tasks {
        println!("  {}", note);
      }
    } else {
      for note in tasks {
        println!("{}", note);
      }
    }
  }
  Ok(())
}