char_coal/
word.rs

1pub mod cached;
2pub mod display;
3pub mod speech;
4pub mod youdict;
5
6use crate::Cache;
7use scraper::ElementRef;
8use serde::{Deserialize, Serialize};
9use whatlang::Lang;
10
11pub struct WordQuery {
12    word: String,
13    lang: Lang,
14}
15
16impl<'a> WordQuery {
17    pub fn new(word: String) -> Option<Self> {
18        if word.is_empty() {
19            return None;
20        }
21        let lang = whatlang::detect(&word).map_or(Lang::Eng, |info| info.lang());
22        Some(Self { word, lang })
23    }
24    pub fn word(&'a self) -> &'a str {
25        &self.word
26    }
27    pub fn is_western(&self) -> bool {
28        !matches!(self.lang, Lang::Cmn | Lang::Jpn)
29    }
30}
31
32#[derive(Clone, Debug, Serialize, Deserialize)]
33pub struct WordEntry {
34    pub pronunciation: Vec<(String, String)>,
35    pub brief: Vec<String>,
36    pub variants: Vec<String>,
37    pub authority: Vec<String>,
38    pub sentence: Vec<(String, String)>,
39}
40
41impl WordEntry {
42    /// Query a word first from cache and then from the web
43    pub async fn query(word_query: &WordQuery, cache: &Cache) -> anyhow::Result<Self> {
44        (FromCache::new(cache).query(&word_query))
45            .or_else(|_err| FromYoudict::new().query_and_store(word_query, cache))
46    }
47
48    pub fn is_empty(&self) -> bool {
49        self.pronunciation.is_empty()
50            && self.brief.is_empty()
51            && self.variants.is_empty()
52            && self.authority.is_empty()
53            && self.sentence.is_empty()
54    }
55}
56
57pub trait Query {
58    fn query(&mut self, word_query: &WordQuery) -> anyhow::Result<WordEntry>;
59}
60
61pub trait Select {
62    type Target;
63    fn select(elem: ElementRef, word_query: &WordQuery) -> anyhow::Result<Self::Target>;
64}
65
66pub struct FromYoudict;
67
68impl FromYoudict {
69    pub fn new() -> Self {
70        Self
71    }
72    pub fn query_and_store(
73        &mut self, word_query: &WordQuery, cache: &Cache,
74    ) -> anyhow::Result<WordEntry> {
75        let word_entry = self.query(word_query)?;
76        let file = cache.store(word_query.word(), "bin")?;
77        bincode::serialize_into(file, &word_entry)?;
78        Ok(word_entry)
79    }
80}
81
82pub struct FromCache<'a> {
83    cache: &'a Cache,
84}
85
86impl<'a> FromCache<'a> {
87    pub fn new(cache: &'a Cache) -> Self {
88        Self { cache }
89    }
90}