deckster 0.2.1

Tui to study flashcards in the terminal
Documentation
use std::fs::File;
use serde_derive::{Serialize,Deserialize};
use std::io::{Read,Write};
use crate::sm2::Rating;
use chrono::{DateTime,Utc};
use crate::errors::Res;

#[derive(Debug,Deserialize,Serialize)]
pub struct Records {
    card: Vec<Record>,
}

impl Records {
    pub fn from(card: Vec<Record>) -> Self {
        Self { card }
    }
    pub fn read_file(file: &str) -> Res<Self> {
        let mut file = File::open(file)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        let records: Records = toml::from_str(&contents)?;
        Ok(records)
    }
    pub fn write_to_file(self,file: &str) -> Res<()> {
        let mut file = File::create(file)?;
        let contents = toml::to_string_pretty(&self).unwrap();
        file.write_all(contents.as_bytes())?;
        Ok(())
    }
    pub fn records(self) -> Vec<Record> {
        self.card
    }
}

#[derive(Debug,Deserialize,Serialize)]
pub struct Record {
    pub question: String,
    pub answer: String,
    pub last_date: Option<String>,
    pub easiness: Option<f32>,
    pub days: Option<f32>,
}

impl Record {
    pub fn review(&mut self, r: Rating) {
        self.update_days(&r);
        self.update_easiness(&r);
        self.update_last_date();
    }
    fn update_easiness(&mut self,r: &Rating) {
        let e = self.easiness.unwrap_or(2.5);
        let e = r.easiness(e);
        self.easiness = Some(e);
    }
    fn update_days(&mut self, r: &Rating) {
        let e = self.easiness.unwrap_or(2.5);
        let d = self.days.unwrap_or(1.0);
        let d = r.days(e,d);
        self.days = Some(d);
    }
    fn update_last_date(&mut self) {
        let ld = Utc::now().to_rfc2822();
        self.last_date = Some(ld);
    }
    pub fn percent_overdue(&self) -> Option<i64> {
        let utc_now = || {
            let now = Utc::now().to_rfc2822();
            DateTime::parse_from_rfc2822(&now).unwrap()
        };

        let last_date = self.last_date.clone().map(|s| {
            DateTime::parse_from_rfc2822(&s)
                .unwrap_or_else(|_| utc_now())
        })?;

        let today = utc_now();
        let diff = today.signed_duration_since(last_date);
        let duration = diff.num_seconds();
        let days = self.days.map(|d| d * 86400.0)? as i64;
        Some(duration/days)
    }
}