1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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(())
}