Skip to content

Commit 0d0f506

Browse files
committed
split view module into get/post
1 parent 9f2a0d2 commit 0d0f506

4 files changed

Lines changed: 166 additions & 148 deletions

File tree

Lines changed: 0 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,17 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use axum::Form;
1615
use axum::extract::State;
1716
use axum::http::StatusCode;
1817
use axum::response::Html;
19-
use axum::response::Redirect;
2018
use maud::Markup;
2119
use maud::PreEscaped;
2220
use maud::html;
23-
use serde::Deserialize;
2421

2522
use crate::drill::state::ServerState;
2623
use crate::drill::template::page_template;
27-
use crate::error::Fallible;
28-
use crate::fsrs::Grade;
2924
use crate::markdown::markdown_to_html;
3025
use crate::types::card::CardContent;
31-
use crate::types::review::Review;
32-
use crate::types::review::update_card;
33-
use crate::types::timestamp::Timestamp;
3426

3527
const CLOZE_TAG: &str = "CLOZE_DELETION";
3628

@@ -166,140 +158,3 @@ pub async fn get_handler(State(state): State<ServerState>) -> (StatusCode, Html<
166158
let html = page_template(body);
167159
(StatusCode::OK, Html(html.into_string()))
168160
}
169-
170-
#[derive(Debug, Deserialize)]
171-
enum Action {
172-
Reveal,
173-
Undo,
174-
End,
175-
Forgot,
176-
Hard,
177-
Good,
178-
Easy,
179-
}
180-
181-
#[derive(Deserialize)]
182-
pub struct FormData {
183-
action: Action,
184-
}
185-
186-
pub async fn post_handler(
187-
State(state): State<ServerState>,
188-
Form(form): Form<FormData>,
189-
) -> Redirect {
190-
match action_handler(state, form.action).await {
191-
Ok(_) => {}
192-
Err(e) => {
193-
log::error!("error: {e}");
194-
}
195-
}
196-
Redirect::to("/")
197-
}
198-
199-
async fn action_handler(state: ServerState, action: Action) -> Fallible<()> {
200-
let mut mutable = state.mutable.lock().unwrap();
201-
let today = state.session_started_at.local_date();
202-
match action {
203-
Action::Reveal => {
204-
if mutable.reveal {
205-
log::error!("Revealing a card that is already revealed.");
206-
} else {
207-
mutable.reveal = true;
208-
}
209-
}
210-
Action::Undo => {
211-
if !mutable.reviewed.is_empty() {
212-
let r = mutable.reviews.pop().unwrap();
213-
let card = if r.grade == Grade::Forgot || r.grade == Grade::Hard {
214-
let _ = mutable.reviewed.pop().unwrap();
215-
mutable.cards.pop().unwrap()
216-
} else {
217-
mutable.reviewed.pop().unwrap()
218-
};
219-
mutable.cards.insert(0, card);
220-
} else {
221-
log::error!("No reviewed cards to undo.");
222-
}
223-
}
224-
Action::End => {
225-
log::debug!("Session completed");
226-
let session_ended_at = Timestamp::now();
227-
let reviews = mutable.reviews.clone();
228-
mutable
229-
.db
230-
.save_session(state.session_started_at, session_ended_at, reviews)?;
231-
mutable.finished = true;
232-
}
233-
Action::Forgot | Action::Hard | Action::Good | Action::Easy => {
234-
if !mutable.reveal {
235-
log::error!("Answering a card that is not revealed.");
236-
} else {
237-
let card = mutable.cards.remove(0);
238-
let hash = card.hash();
239-
let latest_review = match mutable.db.get_latest_review(hash)? {
240-
Some(r) => Some(r),
241-
None => {
242-
// Look through the in-memory review database.
243-
let mut found: Option<Review> = None;
244-
for r in mutable.reviews.iter().rev() {
245-
if r.card_hash == hash {
246-
found = Some(r.clone());
247-
break;
248-
}
249-
}
250-
found
251-
}
252-
};
253-
let grade: Grade = match action {
254-
Action::Forgot => Grade::Forgot,
255-
Action::Hard => Grade::Hard,
256-
Action::Good => Grade::Good,
257-
Action::Easy => Grade::Easy,
258-
_ => unreachable!(),
259-
};
260-
let parameters = update_card(latest_review, grade, today);
261-
let review = Review {
262-
card_hash: hash,
263-
reviewed_at: Timestamp::now(),
264-
grade,
265-
stability: parameters.stability,
266-
difficulty: parameters.difficulty,
267-
due_date: parameters.due_date,
268-
};
269-
mutable.reviews.push(review);
270-
271-
let diff_percent = ((parameters.difficulty - 1.0) / (9.0)) * 100.0;
272-
log::debug!(
273-
"{} {} S={:.2}d D={:.2}% due={}",
274-
&hash.to_hex()[..8],
275-
grade.as_str(),
276-
parameters.stability,
277-
diff_percent,
278-
parameters.due_date.into_inner()
279-
);
280-
281-
// Cards graded `Forgot` or `Hard` are put at the back of the
282-
// queue.
283-
if grade == Grade::Forgot || grade == Grade::Hard {
284-
mutable.cards.push(card.clone());
285-
}
286-
287-
mutable.reviewed.push(card);
288-
289-
mutable.reveal = false;
290-
291-
// Was this the last card?
292-
if mutable.cards.is_empty() {
293-
log::debug!("Session completed");
294-
let session_ended_at = Timestamp::now();
295-
let reviews = mutable.reviews.clone();
296-
mutable
297-
.db
298-
.save_session(state.session_started_at, session_ended_at, reviews)?;
299-
mutable.finished = true;
300-
}
301-
}
302-
}
303-
}
304-
Ok(())
305-
}

src/drill/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
mod get;
16+
mod post;
1517
pub mod server;
1618
mod state;
1719
mod template;
18-
mod view;

src/drill/post.rs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// Copyright 2025 Fernando Borretti
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use axum::Form;
16+
use axum::extract::State;
17+
use axum::response::Redirect;
18+
use serde::Deserialize;
19+
20+
use crate::drill::state::ServerState;
21+
use crate::error::Fallible;
22+
use crate::fsrs::Grade;
23+
use crate::types::review::Review;
24+
use crate::types::review::update_card;
25+
use crate::types::timestamp::Timestamp;
26+
27+
#[derive(Debug, Deserialize)]
28+
enum Action {
29+
Reveal,
30+
Undo,
31+
End,
32+
Forgot,
33+
Hard,
34+
Good,
35+
Easy,
36+
}
37+
38+
#[derive(Deserialize)]
39+
pub struct FormData {
40+
action: Action,
41+
}
42+
43+
pub async fn post_handler(
44+
State(state): State<ServerState>,
45+
Form(form): Form<FormData>,
46+
) -> Redirect {
47+
match action_handler(state, form.action).await {
48+
Ok(_) => {}
49+
Err(e) => {
50+
log::error!("error: {e}");
51+
}
52+
}
53+
Redirect::to("/")
54+
}
55+
56+
async fn action_handler(state: ServerState, action: Action) -> Fallible<()> {
57+
let mut mutable = state.mutable.lock().unwrap();
58+
let today = state.session_started_at.local_date();
59+
match action {
60+
Action::Reveal => {
61+
if mutable.reveal {
62+
log::error!("Revealing a card that is already revealed.");
63+
} else {
64+
mutable.reveal = true;
65+
}
66+
}
67+
Action::Undo => {
68+
if !mutable.reviewed.is_empty() {
69+
let r = mutable.reviews.pop().unwrap();
70+
let card = if r.grade == Grade::Forgot || r.grade == Grade::Hard {
71+
let _ = mutable.reviewed.pop().unwrap();
72+
mutable.cards.pop().unwrap()
73+
} else {
74+
mutable.reviewed.pop().unwrap()
75+
};
76+
mutable.cards.insert(0, card);
77+
} else {
78+
log::error!("No reviewed cards to undo.");
79+
}
80+
}
81+
Action::End => {
82+
log::debug!("Session completed");
83+
let session_ended_at = Timestamp::now();
84+
let reviews = mutable.reviews.clone();
85+
mutable
86+
.db
87+
.save_session(state.session_started_at, session_ended_at, reviews)?;
88+
mutable.finished = true;
89+
}
90+
Action::Forgot | Action::Hard | Action::Good | Action::Easy => {
91+
if !mutable.reveal {
92+
log::error!("Answering a card that is not revealed.");
93+
} else {
94+
let card = mutable.cards.remove(0);
95+
let hash = card.hash();
96+
let latest_review = match mutable.db.get_latest_review(hash)? {
97+
Some(r) => Some(r),
98+
None => {
99+
// Look through the in-memory review database.
100+
let mut found: Option<Review> = None;
101+
for r in mutable.reviews.iter().rev() {
102+
if r.card_hash == hash {
103+
found = Some(r.clone());
104+
break;
105+
}
106+
}
107+
found
108+
}
109+
};
110+
let grade: Grade = match action {
111+
Action::Forgot => Grade::Forgot,
112+
Action::Hard => Grade::Hard,
113+
Action::Good => Grade::Good,
114+
Action::Easy => Grade::Easy,
115+
_ => unreachable!(),
116+
};
117+
let parameters = update_card(latest_review, grade, today);
118+
let review = Review {
119+
card_hash: hash,
120+
reviewed_at: Timestamp::now(),
121+
grade,
122+
stability: parameters.stability,
123+
difficulty: parameters.difficulty,
124+
due_date: parameters.due_date,
125+
};
126+
mutable.reviews.push(review);
127+
128+
let diff_percent = ((parameters.difficulty - 1.0) / (9.0)) * 100.0;
129+
log::debug!(
130+
"{} {} S={:.2}d D={:.2}% due={}",
131+
&hash.to_hex()[..8],
132+
grade.as_str(),
133+
parameters.stability,
134+
diff_percent,
135+
parameters.due_date.into_inner()
136+
);
137+
138+
// Cards graded `Forgot` or `Hard` are put at the back of the
139+
// queue.
140+
if grade == Grade::Forgot || grade == Grade::Hard {
141+
mutable.cards.push(card.clone());
142+
}
143+
144+
mutable.reviewed.push(card);
145+
146+
mutable.reveal = false;
147+
148+
// Was this the last card?
149+
if mutable.cards.is_empty() {
150+
log::debug!("Session completed");
151+
let session_ended_at = Timestamp::now();
152+
let reviews = mutable.reviews.clone();
153+
mutable
154+
.db
155+
.save_session(state.session_started_at, session_ended_at, reviews)?;
156+
mutable.finished = true;
157+
}
158+
}
159+
}
160+
}
161+
Ok(())
162+
}

src/drill/server.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ use tokio::net::TcpStream;
3434
use tokio::time::sleep;
3535

3636
use crate::db::Database;
37+
use crate::drill::get::get_handler;
38+
use crate::drill::post::post_handler;
3739
use crate::drill::state::MutableState;
3840
use crate::drill::state::ServerState;
39-
use crate::drill::view::get_handler;
40-
use crate::drill::view::post_handler;
4141
use crate::error::ErrorReport;
4242
use crate::error::Fallible;
4343
use crate::error::fail;

0 commit comments

Comments
 (0)