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)
}
}