mj 0.4.3

My Journal - personal tool to capture ideas, work with journals, notes and tasks in your favourite text $EDITOR.
Documentation
mod prelude;
mod processor;
mod query;
mod state;
mod view;

use prelude::*;

pub fn start(vault: &Vault) -> Result<()> {
  let mut state = State::SearchIdeas(
    Query::new(vault.list_ideas().unwrap_or(vec![]))
  );
  let mut update_status: Option<UpdateStatus> = None; // Some(UpdateStatus::StartIdeaEditor(String::from("my-journal")));
  loop {
    match update_status {
      None => {
        let mut stdin: Keys<AsyncReader> = termion::async_stdin().keys();
        let stdout = io::stdout();
        let mut term = init_term(stdout.lock())?;
        term.clear()?;
        term.hide_cursor()?;
        let mut first_run = true;
        loop {
          let status = update(&mut stdin, &vault, &mut state)?;
          update_status = Some(status.copy());
          match status {
            UpdateStatus::Changed => view::render(&mut term, &vault, &state)?,
            UpdateStatus::Unchanged => if first_run {
              first_run = false;
              view::render(&mut term, &vault, &state)?;
            }
            UpdateStatus::Quit
              | UpdateStatus::StartIdeaEditor(_)
              | UpdateStatus::StartJournalEditor(_)
              | UpdateStatus::StartNoteEditor(_, _)
              | UpdateStatus::StartTaskEditor(_)
              => break,
          }
          sleep(Duration::from_millis(10));
        }
        term.clear()?;
      },
      Some(UpdateStatus::StartIdeaEditor(name)) => {
        // NOTE: consuming from stdin might resolve the issue of extra input generated into the
        // EDITOR's buffer (apparent the most with `EDITOR="emacs -nw"`)
        // ---
        // let stdin = io::stdin();
        // let mut data = String::new();
        // stdin.read_line(&mut data).unwrap_or(0);
        // ---
        let saved = ideas::edit(&vault, name.to_owned())?;
        if saved {
          if let State::SearchIdeas(query) = &mut state {
            query.results = vault.list_ideas().unwrap_or(vec![]);
            for (i, (x, _)) in query.results.iter().enumerate() {
              if x == &name {
                query.selected_result_index = Some(i);
                break;
              }
            }
            query.input = String::from("");
          }
        }
        update_status = None;
      },
      Some(UpdateStatus::StartJournalEditor(date)) => {
        let saved = journals::edit(&vault, &date)?;
        if saved {
          if let State::SearchJournals(query) = &mut state {
            let name = date.format();
            query.results = vault.list_journals().unwrap_or(vec![]);
            for (i, (x, _)) in query.results.iter().enumerate() {
              if x == &name {
                query.selected_result_index = Some(i);
                break;
              }
            }
            query.input = String::from("");
          }
        }
        update_status = None;
      },
      Some(UpdateStatus::StartNoteEditor(category, name)) => {
        let saved = notes::edit(&vault, &category, &name)?;
        if saved {
          if let State::SearchNotes(query) = &mut state {
            query.results = vault.list_notes(true, &None).unwrap_or(vec![]);
            let results = query.flatten_results();
            for (i, (x, y, _, _)) in results.iter().enumerate() {
              if x == &category && y == &name {
                query.selected_result_index = Some(i);
                break;
              }
            }
            query.input = String::from("");
          }
        }
        update_status = None;
      },
      Some(UpdateStatus::StartTaskEditor(project)) => {
        let saved = tasks::edit(&vault, &project)?;
        if saved {
          if let State::SearchTasks(query) = &mut state {
            query.results = vault.list_tasks(true, &None).unwrap_or(vec![]);
            for (i, (x, _)) in query.results.iter().enumerate() {
              if x == &project {
                query.selected_result_index = Some(i);
                break;
              }
            }
            query.input = String::from("");
          }
        }
        update_status = None;
      },
      Some(UpdateStatus::Quit) | _ => break,
    }
  }
  Ok(())
}

fn init_term<'a>(stdout: io::StdoutLock<'a>) -> Result<Term<'a>> {
  let raw_stdout = stdout.into_raw_mode()?;
  let backend = TermionBackend::new(raw_stdout);
  Ok(Terminal::new(backend)?)
}

fn update(stdin: &mut Keys<AsyncReader>, vault: &Vault, state: &mut State) -> Result<UpdateStatus> {
  if let Some(key) = stdin.next() {
    let key = key?;
    Ok(process(vault, state, key))
  } else {
    Ok(UpdateStatus::Unchanged)
  }
}