diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server.rs | 123 |
1 files changed, 102 insertions, 21 deletions
diff --git a/src/server.rs b/src/server.rs index 58c4dee..c90ee40 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,8 +1,8 @@ use std::fs; use std::io::Write; -use std::sync::Arc; -use anyhow::anyhow; +use futures::stream::TryStreamExt; +use anyhow::{anyhow, Result}; use rand::prelude::*; use http_types::mime; @@ -10,6 +10,7 @@ use tide::Request; use crate::datafiles::*; use crate::format::*; +use crate::example::*; use crate::*; pub async fn server_main() -> tide::Result<()> { @@ -64,7 +65,7 @@ pub async fn server_main() -> tide::Result<()> { // ---- setup http server ---- - let state = Arc::new(StateStruct { + let state: State = Box::leak(Box::new(StateStruct { jmdict_raw, jmdict_xml, jmdict_idx, @@ -73,7 +74,7 @@ pub async fn server_main() -> tide::Result<()> { examples, example_freq, furigana_overrides, - }); + })); let mut app = tide::with_state(state); app.with(tide::log::LogMiddleware::new()); @@ -81,6 +82,8 @@ pub async fn server_main() -> tide::Result<()> { app.at("/").get(home_page); app.at("/index.html").get(home_page); app.at("/style.css").serve_file("static/style.css")?; + app.at("/script.js").serve_file("static/script.js")?; + app.at("/jquery.js").serve_file("static/jquery.js")?; app.at("/about.html").get(about_page); app.at("/ex/:start/:end").get(gen_examples_page); app.at("/:batch").get(batch_page); @@ -93,7 +96,7 @@ pub async fn server_main() -> tide::Result<()> { Ok(()) } -type State = Arc<StateStruct>; +type State = &'static StateStruct; #[allow(dead_code)] struct StateStruct { jmdict_raw: &'static str, @@ -172,18 +175,92 @@ async fn gen_examples_page(req: Request<State>) -> tide::Result { .flatten(), ); - let mut examples = gen_examples(&req.state(), &allowed_chars, &needed_chars, 50); - for ex in examples.iter_mut() { - ex.gen_furigana(&req.state().jmdict_idx, &req.state().furigana_overrides); - } + let (tx, rx) = async_channel::unbounded(); + + let state: State = req.state(); + std::thread::spawn(move || { + tx.send_blocking(Ok(format!(r#" + <!DOCTYPE html> + <html> + <head> + <meta charset=\"UTF-8\" /> + <title>{:03} - {:03} practice</title> + <link rel="stylesheet" type="text/css" href="/style.css" /> + <script src="/jquery.js"></script> + <script src="/script.js"></script> + </head> + <body> + <div class="batch_page"> + <p><a href="index.html">index</a></p> + <p>Practice for {:03} - {:03}</p> + <hr /> + <div id="gen_section"> + <div id="gen_ex_cnt"> + </div> + <div id="gen_ex_display"> + </div> + <div id="gen_ex_en"> + </div> + <div id="gen_ex_words" class="vocabtable"> + </div> + </div> + </div> + </body> + "#, first_level, last_level, first_level, last_level).into_bytes()))?; + + gen_examples(state, &allowed_chars, &needed_chars, 50, |mut ex| { + ex.gen_furigana(&req.state().jmdict_idx, &req.state().furigana_overrides); + + let mut expl = "<table>".to_string(); + for word in ex.expl.split(|c| c == ' ' || c == '~') { + let (keb, reb) = expl_clean_word(word); + let wchars = Charset::new(keb); + if !wchars.intersects(&allowed_chars) { + continue; + } + if let Some(ents) = state.jmdict_idx.get(keb) { + for ent in ents.iter() { + let ent_r_ele = ent.children().find(|x| x.has_tag_name("r_ele")).unwrap(); + let ent_reb = ent_r_ele.children().find(|x| x.has_tag_name("reb")).unwrap(); + let ent_reb = ent_reb.text().unwrap().trim(); + if reb.map(|x| x != ent_reb).unwrap_or(false) { + continue; + } + expl += &format!(r#"<tr><td style="word-break: keep-all"> <span class="tab_large font_ja">{}</span> </td><td width="50%">"#, keb); + + for sense in ent.children().filter(|x| x.has_tag_name("sense")) { + if let Some(s) = sense.children().find(|x| x.has_tag_name("gloss")) { + if !expl.ends_with('>') { + expl += "; "; + } + expl += s.text().unwrap().trim(); + } + } + expl += &format!(r#"</td><td style="word-break: keep-all" class="tab_large font_ja">{}</td></tr>"#, ent_reb); + } + } + } - let mut buf: Vec<u8> = vec![]; - for ex in examples.iter() { - write!(&mut buf, "<p>{}</p>", ex.furigana_markup())?; - } + let item = serde_json::json!({ + "ja": ex.ja, + "en": ex.en, + "furi": ex.furigana_markup(), + "vocab": expl + "</table>", + }); + tx.send_blocking(Ok(format!("<script> add_example({}); </script>\n", serde_json::to_string(&item)?).into_bytes()))?; + Ok(()) + })?; + + tx.send_blocking(Ok(br#" + </body> + </html> + "#.to_vec()))?; + + Ok::<_, anyhow::Error>(()) + }); Ok(tide::Response::builder(200) - .body(buf) + .body(tide::Body::from_reader(Box::pin(rx).into_async_read(), None)) .content_type(mime::HTML) .build()) } @@ -200,14 +277,16 @@ fn calc_example_freq(examples: &[Example]) -> HashMap<char, usize> { ret } -fn gen_examples( +fn gen_examples<F>( data: &StateStruct, allowed_chars: &Charset, needed_chars: &Charset, count: usize, -) -> Vec<Example> { + mut callback: F, +) -> Result<()> +where F: FnMut(Example) -> Result<()> { let mut rng = thread_rng(); - let mut ret = vec![]; + let mut generated = 0; let mut candidates = data .examples @@ -219,7 +298,7 @@ fn gen_examples( let mut have_chars = Charset::new(""); println!("Ex\tMinCnt\tChars\tNeeded\tAllowed\tCandidates\tChars"); - while ret.len() < count { + while generated < count { let mut selection = None; let mut total_weight = 0f64; @@ -245,11 +324,11 @@ fn gen_examples( let (ex, _) = candidates.remove(i); remaining_needed = remaining_needed.diff(&ex.chars); have_chars = have_chars.union(&ex.chars); - ret.push(ex.clone()); + generated += 1; println!( "{}\t{}\t{}\t{}\t{}\t{}\t{}", - ret.len(), + generated, f, have_chars.len(), remaining_needed.len(), @@ -257,10 +336,12 @@ fn gen_examples( counted, ex.chars.to_string() ); + + callback(ex.clone())?; } else { break; } } - ret + Ok(()) } |