-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathserve.rs
159 lines (135 loc) · 6.22 KB
/
serve.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use crate::prelude::*;
use http::status::StatusCode;
use http_service::Body;
use std::sync::Mutex;
use uuid::Uuid;
#[derive(rust_embed::RustEmbed)]
#[folder = "mathema/build"]
struct Asset;
async fn serve_cards(cx: tide::Context<Mutex<MathemaRepository>>) -> tide::EndpointResult {
let repo = cx.app_data().lock().unwrap();
eprintln!("serve_cards");
let uuids: Vec<Uuid> = repo.cards().keys().cloned().collect();
Ok(tide::response::json(uuids))
}
async fn serve_card(cx: tide::Context<Mutex<MathemaRepository>>) -> tide::EndpointResult {
let repo = cx.app_data().lock().unwrap();
let uuid: Uuid = cx.param("uuid").map_err(|_| StatusCode::BAD_REQUEST)?;
eprintln!("serve_card uuid={}", uuid);
let card = repo.card(uuid);
Ok(tide::response::json(card))
}
async fn quiz_cards(cx: tide::Context<Mutex<MathemaRepository>>) -> tide::EndpointResult {
let repo = cx.app_data().lock().unwrap();
let language: Language = cx.param("lang").map_err(|_| StatusCode::BAD_REQUEST)?;
eprintln!("quiz_cards language={:?}", language);
let suitable_questions = SUITABLE_QUESTIONS
.iter()
.filter_map(|(l, qks)| if *l == language { Some(*qks) } else { None })
.next()
.ok_or(StatusCode::BAD_REQUEST)?;
let rng = &mut rand::thread_rng();
let cards = selection::expired_cards(rng, &repo, &suitable_questions);
eprintln!("cards={:?}", cards.len());
Ok(tide::response::json(cards))
}
async fn transliterate(cx: tide::Context<Mutex<MathemaRepository>>) -> tide::EndpointResult {
eprintln!("transliterate");
let language: Language = cx.param("lang").map_err(|_| StatusCode::BAD_REQUEST)?;
eprintln!("transliterate={:?}", language);
// FIXME(tide) -- this all looks like tide bugs to me
let text: String = cx.param("text*").map_err(|_| StatusCode::BAD_REQUEST)?;
let text: String = percent_encoding::percent_decode(text.as_bytes()).decode_utf8().map_err(|_| StatusCode::BAD_REQUEST)?.into_owned();
eprintln!("transliterate={:?}", text);
let out_text = language.transliterate(&text);
eprintln!("transliterate={:?}", out_text);
Ok(tide::response::json(out_text))
}
async fn check_answer(cx: tide::Context<Mutex<MathemaRepository>>) -> tide::EndpointResult {
eprintln!("check_answer");
let expected: String = cx.param("expected").map_err(|_| StatusCode::BAD_REQUEST)?;
let expected: String = percent_encoding::percent_decode(expected.as_bytes()).decode_utf8().map_err(|_| StatusCode::BAD_REQUEST)?.into_owned(); // TIDE bug?
let user: String = cx.param("user").map_err(|_| StatusCode::BAD_REQUEST)?;
let user: String = percent_encoding::percent_decode(user.as_bytes()).decode_utf8().map_err(|_| StatusCode::BAD_REQUEST)?.into_owned(); // TIDE bug?
let result = quiz::check_user_response(&expected, &user);
eprintln!("expected={:?} user={:?} result={:?}", expected, user, result);
Ok(tide::response::json(result))
}
async fn mark_answer(cx: tide::Context<Mutex<MathemaRepository>>) -> tide::EndpointResult {
eprintln!("mark_answer");
let uuid: Uuid = cx.param("uuid").map_err(|_| StatusCode::BAD_REQUEST)?;
let from: Language = cx.param("from").map_err(|_| StatusCode::BAD_REQUEST)?;
let to: Language = cx.param("to").map_err(|_| StatusCode::BAD_REQUEST)?;
let response: QuestionResult = cx.param("response").map_err(|_| StatusCode::BAD_REQUEST)?;
let question_kind = QuestionKind::Translate { from, to };
let mut repo = cx.app_data().lock().unwrap();
let record = repo.database_mut().card_record_mut(uuid);
record.push_question_record(
question_kind,
QuestionRecord {
date: Utc::now(),
result: response,
},
);
Ok(tide::response::json("ok"))
}
async fn write_db(cx: tide::Context<Mutex<MathemaRepository>>) -> tide::EndpointResult {
eprintln!("write_db");
let mut repo = cx.app_data().lock().unwrap();
repo.write_database().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(tide::response::json("ok"))
}
async fn serve_asset(cx: tide::Context<Mutex<MathemaRepository>>) -> tide::EndpointResult {
eprintln!("serve_asset(cx.uri().path() = {:?})", cx.uri().path());
serve_path(cx.uri().path()).await
}
async fn serve_index(cx: tide::Context<Mutex<MathemaRepository>>) -> tide::EndpointResult {
eprintln!("serve_index(cx.uri().path() = {:?})", cx.uri().path());
serve_path("index.html").await
}
async fn serve_path(path: &str) -> tide::EndpointResult {
let data = Asset::get(&path[1..]).ok_or(StatusCode::NOT_FOUND)?;
let content_type: Option<&str> = try {
match Path::new(path).extension()?.to_str()? {
"json" => "application/json",
"html" => "text/html;charset=UTF-8",
"js" => "text/javascript;charset=UTF-8",
"ico" => "image/vnd.microsoft.icon",
"css" => "text/css;charset=UTF-8",
_ => None?,
}
};
Ok(
http::Response::builder()
.status(http::status::StatusCode::OK)
.header("Content-Type", content_type.unwrap_or("application/octet-stream"))
.body(Body::from(&data[..]))
.unwrap()
)
}
crate fn serve(options: &MathemaOptions) -> Fallible<()> {
try {
let mut repo = MathemaRepository::open(options)?;
let status = repo.load_cards()?;
if status.warn_if_needed(options.force) {
return Ok(());
}
let mut app = tide::App::new(Mutex::new(repo));
app.at("/api/cards").get(serve_cards);
app.at("/api/card/:uuid").get(serve_card);
app.at("/api/quiz_cards/:lang").get(quiz_cards);
app.at("/api/transliterate/:lang/:text*").get(transliterate);
app.at("/api/check_answer/:expected/:user").get(check_answer);
app.at("/api/mark_answer/:uuid/translate/:from/:to/:response").post(mark_answer);
app.at("/api/write_db").post(write_db);
// Register the static assets. I don't think that tide supports a fallback,
// which is annoying.
app.at("/").get(serve_index);
for file in Asset::iter() {
assert!(!file.contains(":"));
app.at(&file).get(serve_asset);
eprintln!("serving assert {:?}", file);
}
app.serve("127.0.0.1:8000")?;
}
}