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
mod sm2;
mod records;
mod slide;
mod events;
mod cards;
pub mod errors;

use records::*;
use slide::Slide;
use std::io;
use termion::raw::IntoRawMode;
use termion::event::Key;
use tui::Terminal;
use tui::backend::TermionBackend;
use tui::layout::{Constraint,Direction,Layout};
use events::Event;
use errors::Res;

const DEFAULT: usize = 20;

pub fn start_tui(file : &str, num: Option<usize>) -> Res<()> {
    let stdout = io::stdout().into_raw_mode()?;
    let backend = TermionBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;

    let events = events::setup_events();
    let records = Records::read_file(file)?.records();

    let num = num.unwrap_or(DEFAULT);
    let mut cards = cards::Cards::from(records).select(num);

    terminal.clear()?;

    while let Some(_) = cards.next() {
        let record = cards.current_record();
        let mut slide = Slide::default().record(record);

        let mut exit_called = false;
        let mut exit = || exit_called = true;

        while let Some(step) = slide.step() {
            let final_step = step == 2;
            let record = slide.clone();

            terminal.draw(|f| {
                let h2 = 3u16;
                let h1 = f.size().height - h2;

                let section = Layout::default()
                    .direction(Direction::Vertical)
                    .constraints(
                        [
                            Constraint::Length(h1),
                            Constraint::Length(h2),
                        ]
                    ).split(f.size());

                f.render_widget(record.clone(),section[0]);
                f.render_widget(cards.status(),section[1]);
            })?;

            match events.recv().unwrap() {
                Event::Refresh => (),
                Event::Key(key) => match key {
                    Key::Char(' ') =>  if !final_step { slide.cont() },
                    Key::Char('\n') => if final_step { slide.cont() },
                    Key::Char('l') | Key::Right => if final_step {slide.next()},
                    Key::Char('h') | Key::Left =>  if final_step {slide.prev()},
                    Key::Char('q') | Key::Esc => { exit(); break },
                    Key::Ctrl('c') | Key::Ctrl('d') => { exit(); break },
                    _ => (),
                },
            };
        }
        if exit_called { break }

        let rating = slide.rating();
        cards.review_current_card(rating);
    };

    let records = cards.records();
    let records = Records::from(records);
    records.write_to_file(file)?;
    terminal.clear()?;
    Ok(())
}

pub fn generate_sample() -> Res<()> {
    let sample =
r#"
# Structure each new card in the format below.
# Deckster will generate additional metadata for each
# card and store it in this file. Avoid editing
# the metadata.
#
# Uncomment the block below to begin.
#
# [[card]]
# question = "question 1"
# answer = "answer 1"
"#;
    println!("{}",sample);
    Ok(())
}