cargo-todo-rs 0.1.0

A simple todo Cli app.
use std::io::{Write, Read};
use ncurses::*;

const REGULAR_PAIR: i16 = 0;
const HIGHLIGHTED_PAIR: i16 = 1;

#[derive(Default)]
struct Manage {
    todos: Vec<String>,
    dones: Vec<String>
}
impl Manage {

    // Load the list from an external file.
    fn initialize(&mut self) {
        let mut file = match std::fs::File::open("TO-DO") {
            Ok(file) => file,
            Err(_) => return
        };
        let mut contents = String::new();
        file.read_to_string(&mut contents).expect("Failed to read");
        for line in contents.lines() {
            if line == "" { continue; }
            let line = line.to_string();
            if line.starts_with("TODO:") {
                self.todos.push(line[6..].to_string());
            } else {
                self.dones.push(line[6..].to_string());
            }
        };
    }

    // Generating a frame based on the current data.
    fn make_frame(&mut self, tab: bool, highlight: usize) {
        clear();
        if tab {
            addstr("[TODO]   DONE ");
            for (index, todo) in self.todos.iter().enumerate() {
                let pair = if highlight == index {HIGHLIGHTED_PAIR}
                else {REGULAR_PAIR};
                mv((index+1).try_into().unwrap(), 0);
                attr_on(COLOR_PAIR(pair));
                addstr("[ ] - ");
                addstr(todo);  
                attr_off(COLOR_PAIR(pair));
            }
            mv((self.todos.len() + 1).try_into().unwrap(), 0);
            addstr("------------------------------");
            mv((self.todos.len() + 2).try_into().unwrap(), 0);
            addstr("Add new task (a): ");
        } else {
            addstr(" TODO   [DONE]");
            for (index, done) in self.dones.iter().enumerate() {
                let pair = if highlight == index {HIGHLIGHTED_PAIR}
                else {REGULAR_PAIR};
                mv((index+1).try_into().unwrap(), 0);
                attr_on(COLOR_PAIR(pair));
                addstr("[*] - ");
                addstr(done);  
                attr_off(COLOR_PAIR(pair));
            }
        }
    }

    // Transfering a task from todo to done or vice-versa.
    fn swap(&mut self, tab: bool, highlight: usize) {
        if tab {
            if self.todos.len() > 0 {
                self.dones.push(self.todos.get(highlight).unwrap().to_string());
                self.todos.swap_remove(highlight);
            }  
        } else {
            if self.dones.len() > 0 {
                self.todos.push(self.dones.get(highlight).unwrap().to_string());
                self.dones.swap_remove(highlight);
            }
        }
    }

    // Adding a new task to the todo list.
    fn add_task(&mut self) {
        curs_set(CURSOR_VISIBILITY::CURSOR_VISIBLE);
        mv((self.todos.len() + 2).try_into().unwrap(), 18);
        let mut new_task = String::new();
        getstr(&mut new_task);
        self.todos.push(new_task);
        curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);
    }

    // Deleting the highlighted task.
    fn delete(&mut self, highlight: usize) {
        self.todos.swap_remove(highlight);
    }

    // Loads the data into a file before quitting.
    fn end(&mut self) {
        let mut file = std::fs::File::create("TO-DO")
        .expect("Create Failed");
        for todo in self.todos.iter(){
            file.write_all(format!("TODO: {}\n", todo).as_bytes())
            .expect("Write Failed");
        }
        for done in self.dones.iter(){
            file.write_all(format!("DONE: {}\n", done).as_bytes())
            .expect("Write Failed");
        }
    }
}

// Main function that manages everything.
fn main() {
    let mut ui = Manage::default();
    ui.initialize();

    initscr();
    start_color();
    // Making the cursor invisible.
    curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);
    // Initializing different color pairs.
    init_pair(REGULAR_PAIR, COLOR_WHITE, COLOR_BLACK);
    init_pair(HIGHLIGHTED_PAIR, COLOR_BLACK, COLOR_WHITE);

    let mut quit = false;
    let mut tab = true;
    let mut curr_high = 0;
    while !quit {
        ui.make_frame(tab, curr_high);
        // Updadting the screen.
        refresh();
        // Waiting for the input and then getting it.
        let key = getch(); 
        // matching the input to different functions.
        match key as u8 as char {
            'q' => quit = true,
            // Switching between todo, done ans add.
            '\t' => if tab{tab = false; curr_high = 0}
                else {tab = true; curr_high = 0;},
            // Scrolling up.
            'w' => if curr_high > 0 { curr_high -= 1; },
            // Scrolling Down.
            's' => if tab {
                if curr_high < ui.todos.len() - 1 {curr_high += 1;}
            } else {
                if curr_high < ui.dones.len() - 1 { curr_high += 1;}
            },
            // Making changes when enter (return) is pressed.
            '\n' => {
                ui.swap(tab, curr_high); 
                if curr_high > 0 {
                    curr_high -= 1;
                }
            },
            'a' => if tab { ui.add_task() },
            'd' => if tab {
                ui.delete(curr_high);
                if curr_high > 0 {
                    curr_high -= 1;
                }
            },
            _ => {}
        }
    }

    ui.end();
    // Making the cursor visible.
    curs_set(CURSOR_VISIBILITY::CURSOR_VISIBLE);
    // Closing the window.
    endwin();
}