Skip to content

Commit

Permalink
chore: Add the state machine for our FlashCard
Browse files Browse the repository at this point in the history
  • Loading branch information
nvh0412 committed Mar 16, 2024
1 parent 104de91 commit 3820df7
Show file tree
Hide file tree
Showing 22 changed files with 2,443 additions and 194 deletions.
2,100 changes: 2,056 additions & 44 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ log = "0.4.20"
catppuccin = "1.4.0"
rust-embed = "8.2.0"
anyhow = "1.0.80"
fsrs = { git = "https://github.com/open-spaced-repetition/fsrs-rs" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.114"
2 changes: 1 addition & 1 deletion src/components/deck/deck_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ impl RenderOnce for HocListItem {
.p_2()
.border_1()
.rounded_xl()
.on_mouse_down(gpui::MouseButton::Left, move |e, cx| {
.on_mouse_down(gpui::MouseButton::Left, move |_e, cx| {
StackableViewState::update(
|state, cx| {
state.push(
Expand Down
4 changes: 2 additions & 2 deletions src/components/tab_bar_container.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{cell::RefCell, rc::Rc};

use gpui::{div, prelude::*, EventEmitter, Render, StyleRefinement, View, WindowContext};
use gpui::{div, prelude::*, EventEmitter, Render, View, WindowContext};

use crate::theme::Theme;

Expand Down Expand Up @@ -42,7 +42,7 @@ pub enum TabEvent {
impl EventEmitter<TabEvent> for TabBarView {}

impl Render for TabBarView {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {}
fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {}
}

impl RenderOnce for TabBarContainer {
Expand Down
1 change: 1 addition & 0 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub fn init_db(conn: &Connection) -> Result<()> {
interval INTEGER NOT NULL,
queue integer NOT NULL,
due integer NOT NULL,
data text NOT NULL,
FOREIGN KEY(deck_id) REFERENCES decks(id)
)",
[],
Expand Down
25 changes: 25 additions & 0 deletions src/models/card.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use crate::{repositories::flash_card::CardQueue, FlashCard};

use super::states::{
card_state::CardState, learning_state::LearningState, new_state::NewState,
review_state::ReviewState,
};

pub fn get_current_card_state(card: &FlashCard) -> CardState {
// get due from the card
let due = card.due;
let interval = card.interval;
let ef = card.ease_factor();

match *card.get_queue() {
CardQueue::New => NewState {}.into(),
CardQueue::Learning => LearningState {
remaining_steps: 0,
scheduled_secs: 0,
elapsed_secs: 0,
memory_state: None,
}
.into(),
CardQueue::Review => ReviewState {}.into(),
}
}
11 changes: 1 addition & 10 deletions src/models/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,18 @@ use gpui::{AppContext, Global};

use crate::{errors::Result, storage::sqlite::SqliteStorage};

use super::{
builder::Builder,
queue::{Queue, QueueBuilder},
};
use super::{builder::Builder, queue::Queue};

pub struct CollectionBuilder {
collection_path: Option<PathBuf>,
deck_id: Option<usize>,
}

impl CollectionBuilder {
pub fn new(col_path: PathBuf) -> Self {
CollectionBuilder {
collection_path: Some(col_path),
deck_id: None,
}
}

pub fn set_deck_id(&mut self, deck_id: usize) {
self.deck_id = Some(deck_id);
}
}

impl Builder for CollectionBuilder {
Expand Down
7 changes: 1 addition & 6 deletions src/models/deck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ pub fn get_decks(conn: &Connection) -> Vec<Deck> {
let decks_res = Deck::get_all_decks(&conn);

match decks_res {
Ok(mut decks) => {
decks.iter_mut().for_each(|d| {
d.cards = FlashCard::get_all_cards_in_deck(d.id.unwrap(), &conn, 10).unwrap();
});
decks
}
Ok(mut decks) => decks,
Err(e) => {
eprintln!("Error getting decks: {}", e);
vec![]
Expand Down
6 changes: 4 additions & 2 deletions src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pub mod deck;
pub mod builder;
pub mod card;
pub mod collection;
pub mod deck;
pub mod queue;
pub mod builder;
pub mod states;
52 changes: 34 additions & 18 deletions src/models/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,37 @@ use rusqlite::Connection;

use crate::{errors::Result, repositories::flash_card::CardQueue, FlashCard};

use super::{builder::Builder, collection::Collection, deck};
use super::{
builder::Builder, card::get_current_card_state, collection::Collection,
states::card_state::CardState,
};

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Stats {
pub new: usize,
pub learning: usize,
pub review: usize,
}

#[derive(Debug)]
#[derive(Clone)]
pub struct Queue {
pub stats: Stats,
pub core: VecDeque<QueueEntry>,
}

#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct SchedulingStates {
pub current: CardState,
pub again: CardState,
pub hard: CardState,
pub good: CardState,
pub easy: CardState,
}

#[derive(Clone)]
pub struct QueueEntry {
pub card_id: u32,
pub states: SchedulingStates,
}

pub struct QueueBuilder {
Expand All @@ -46,27 +59,33 @@ impl QueueBuilder {
}

fn collect_new_cards(&mut self, conn: &Connection) {
FlashCard::for_each_new_card_in_deck(&conn, self.deck_id, CardQueue::New, |card| {
FlashCard::for_each_card_in_deck(&conn, self.deck_id, CardQueue::New, |card| {
self.new.push(card.clone());
})
.unwrap_or_else(|e| {
println!("Error collecting new cards: {:?}", e);
});

FlashCard::for_each_new_card_in_deck(&conn, self.deck_id, CardQueue::Learning, |card| {
FlashCard::for_each_card_in_deck(&conn, self.deck_id, CardQueue::Learning, |card| {
self.learning.push(card.clone());
})
.unwrap_or_else(|e| {
println!("Error collecting learning cards: {:?}", e);
});

FlashCard::for_each_new_card_in_deck(&conn, self.deck_id, CardQueue::Review, |card| {
FlashCard::for_each_card_in_deck(&conn, self.deck_id, CardQueue::Review, |card| {
self.review.push(card.clone());
})
.unwrap_or_else(|e| {
println!("Error collecting learning cards: {:?}", e);
});
}

fn get_scheduling_states(&self, card: &FlashCard) -> SchedulingStates {
let current_state: CardState = get_current_card_state(card);

current_state.next_states()
}
}

impl Builder for QueueBuilder {
Expand All @@ -79,21 +98,18 @@ impl Builder for QueueBuilder {

let mut core_queue: VecDeque<QueueEntry> = VecDeque::new();

self.review.iter().for_each(|card| {
core_queue.push_back(QueueEntry {
card_id: card.id.unwrap(),
});
});
let cards = self
.review
.iter()
.chain(self.learning.iter())
.chain(self.new.iter());

self.learning.iter().for_each(|card| {
core_queue.push_back(QueueEntry {
card_id: card.id.unwrap(),
});
});
cards.for_each(|card| {
let states = self.get_scheduling_states(card);

self.new.iter().for_each(|card| {
core_queue.push_back(QueueEntry {
card_id: card.id.unwrap(),
states,
});
});

Expand Down
29 changes: 29 additions & 0 deletions src/models/states/card_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use crate::models::queue::SchedulingStates;

use super::{
learning_state::LearningState, new_state::NewState, relearning_state::ReLearningState,
review_state::ReviewState,
};

#[derive(Clone)]
pub enum CardState {
New(NewState),
Learning(LearningState),
Review(ReviewState),
ReLearning(ReLearningState),
}

pub trait CardStateTrait {
fn next_states(self) -> SchedulingStates;
}

impl CardState {
pub fn next_states(self) -> SchedulingStates {
match self {
CardState::New(state) => state.next_states(),
CardState::Learning(state) => state.next_states(),
CardState::Review(state) => state.next_states(),
CardState::ReLearning(state) => state.next_states(),
}
}
}
65 changes: 65 additions & 0 deletions src/models/states/learning_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use fsrs::MemoryState;

use super::card_state::{CardState, CardStateTrait};
use crate::models::queue::SchedulingStates;

#[derive(Clone)]
pub struct LearningState {
pub remaining_steps: u32,
pub scheduled_secs: u32,
pub elapsed_secs: u32,
pub memory_state: Option<MemoryState>,
}

impl CardStateTrait for LearningState {
fn next_states(self) -> SchedulingStates {
SchedulingStates {
again: self.answer_again().into(),
hard: self.answer_hard().into(),
good: self.answer_good().into(),
easy: self.answer_easy().into(),
current: self.into(),
}
}
}

impl LearningState {
fn answer_again(&self) -> LearningState {
LearningState {
remaining_steps: 0,
scheduled_secs: 0,
elapsed_secs: 0,
memory_state: None,
}
}
fn answer_hard(&self) -> LearningState {
LearningState {
remaining_steps: 0,
scheduled_secs: 0,
elapsed_secs: 0,
memory_state: None,
}
}
fn answer_good(&self) -> LearningState {
LearningState {
remaining_steps: 0,
scheduled_secs: 0,
elapsed_secs: 0,
memory_state: None,
}
}
fn answer_easy(&self) -> LearningState {
LearningState {
remaining_steps: 0,
scheduled_secs: 0,
elapsed_secs: 0,
memory_state: None,
}
}
}

impl Into<CardState> for LearningState {
fn into(self) -> CardState {
CardState::Learning(self)
}
}
5 changes: 5 additions & 0 deletions src/models/states/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod card_state;
pub mod learning_state;
pub mod new_state;
pub mod relearning_state;
pub mod review_state;
32 changes: 32 additions & 0 deletions src/models/states/new_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::models::queue::SchedulingStates;

use super::{
card_state::{CardState, CardStateTrait},
learning_state::LearningState,
};

#[derive(Clone)]
pub struct NewState {}

impl CardStateTrait for NewState {
fn next_states(self) -> SchedulingStates {
let next_state = LearningState {
remaining_steps: 0,
scheduled_secs: 0,
elapsed_secs: 0,
memory_state: None,
}
.next_states();

SchedulingStates {
current: self.into(),
..next_state
}
}
}

impl Into<CardState> for NewState {
fn into(self) -> CardState {
CardState::New(self)
}
}
12 changes: 12 additions & 0 deletions src/models/states/relearning_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use crate::models::queue::SchedulingStates;

use super::card_state::CardStateTrait;

#[derive(Clone)]
pub struct ReLearningState {}

impl CardStateTrait for ReLearningState {
fn next_states(self) -> SchedulingStates {
todo!()
}
}
18 changes: 18 additions & 0 deletions src/models/states/review_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::models::queue::SchedulingStates;

use super::card_state::{CardState, CardStateTrait};

#[derive(Clone)]
pub struct ReviewState {}

impl CardStateTrait for ReviewState {
fn next_states(self) -> SchedulingStates {
todo!()
}
}

impl Into<CardState> for ReviewState {
fn into(self) -> CardState {
CardState::Review(self)
}
}
Loading

0 comments on commit 3820df7

Please sign in to comment.