houhou/src-tauri/src/kanji.rs

213 lines
4.8 KiB
Rust
Raw Normal View History

2023-06-11 20:08:17 +00:00
use std::collections::HashMap;
2023-06-11 20:08:17 +00:00
use sqlx::{sqlite::SqliteRow, Encode, Row, SqlitePool, Type};
2023-06-11 20:08:15 +00:00
use tauri::State;
2023-06-11 20:08:17 +00:00
use crate::{
srs::SrsDb,
utils::{EpochMs, Ticks},
};
2023-06-11 20:08:15 +00:00
pub struct KanjiDb(pub SqlitePool);
2023-06-11 20:08:15 +00:00
#[derive(Debug, Derivative, Serialize, Deserialize)]
#[derivative(Default)]
pub struct GetKanjiOptions {
2023-06-11 20:08:15 +00:00
#[serde(default)]
2023-06-11 20:08:17 +00:00
character: Option<String>,
2023-06-11 20:08:18 +00:00
#[serde(default = "default_skip")]
#[derivative(Default(value = "0"))]
skip: u32,
2023-06-11 20:08:17 +00:00
#[serde(default = "default_how_many")]
2023-06-11 20:08:18 +00:00
#[derivative(Default(value = "40"))]
2023-06-11 20:08:15 +00:00
how_many: u32,
2023-06-11 20:08:17 +00:00
#[serde(default)]
include_srs_info: bool,
2023-06-11 20:08:15 +00:00
}
2023-06-11 20:08:18 +00:00
fn default_skip() -> u32 {
0
}
2023-06-11 20:08:17 +00:00
fn default_how_many() -> u32 {
2023-06-11 20:08:18 +00:00
40
2023-06-11 20:08:17 +00:00
}
2023-06-11 20:08:15 +00:00
#[derive(Debug, Serialize, Deserialize)]
pub struct Kanji {
character: String,
most_used_rank: u32,
2023-06-11 20:08:17 +00:00
meanings: Vec<KanjiMeaning>,
2023-06-11 20:08:17 +00:00
srs_info: Option<KanjiSrsInfo>,
2023-06-11 20:08:17 +00:00
}
#[derive(Debug, Serialize, Deserialize)]
pub struct KanjiMeaning {
id: u32,
meaning: String,
2023-06-11 20:08:15 +00:00
}
2023-06-11 20:08:15 +00:00
#[derive(Debug, Serialize, Deserialize)]
pub struct GetKanjiResult {
count: u32,
2023-06-11 20:08:15 +00:00
kanji: Vec<Kanji>,
2023-06-11 20:08:15 +00:00
}
2023-06-11 20:08:17 +00:00
#[derive(Debug, Serialize, Deserialize)]
pub struct KanjiSrsInfo {
id: u32,
2023-06-11 20:08:19 +00:00
current_grade: u32,
2023-06-11 20:08:17 +00:00
next_answer_date: EpochMs,
associated_kanji: String,
}
2023-06-11 20:08:15 +00:00
#[tauri::command]
2023-06-11 20:08:15 +00:00
pub async fn get_kanji(
2023-06-11 20:08:17 +00:00
kanji_db: State<'_, KanjiDb>,
srs_db: State<'_, SrsDb>,
2023-06-11 20:08:15 +00:00
options: Option<GetKanjiOptions>,
2023-06-11 20:08:17 +00:00
) -> Result<GetKanjiResult, String> {
2023-06-11 20:08:15 +00:00
let opts = options.unwrap_or_default();
2023-06-11 20:08:17 +00:00
let looking_for_character_clause = match opts.character {
None => String::new(),
2023-06-11 20:08:17 +00:00
Some(_) => format!("AND KanjiSet.Character = ?"),
2023-06-11 20:08:17 +00:00
};
let query_string = format!(
2023-06-11 20:08:15 +00:00
r#"
2023-06-11 20:08:17 +00:00
SELECT
Character,
KanjiMeaningSet.Meaning,
MostUsedRank,
KanjiMeaningSet.ID as KanjiMeaningID
2023-06-11 20:08:17 +00:00
FROM (
SELECT *
FROM KanjiSet
WHERE MostUsedRank IS NOT NULL
{looking_for_character_clause}
2023-06-11 20:08:18 +00:00
ORDER BY MostUsedRank LIMIT ?, ?
2023-06-11 20:08:17 +00:00
) as Kanji
JOIN KanjiMeaningSet ON Kanji.ID = KanjiMeaningSet.Kanji_ID
ORDER BY MostUsedRank, KanjiMeaningSet.ID
2023-06-11 20:08:15 +00:00
"#,
2023-06-11 20:08:17 +00:00
);
2023-06-11 20:08:15 +00:00
2023-06-11 20:08:17 +00:00
let mut query = sqlx::query(&query_string);
2023-06-11 20:08:15 +00:00
2023-06-11 20:08:17 +00:00
// Do all the binds
2023-06-11 20:08:17 +00:00
if let Some(character) = &opts.character {
query = query.bind(character.clone());
2023-06-11 20:08:17 +00:00
}
2023-06-11 20:08:18 +00:00
query = query.bind(opts.skip);
2023-06-11 20:08:17 +00:00
query = query.bind(opts.how_many);
let result = query
2023-06-11 20:08:17 +00:00
.fetch_all(&kanji_db.0)
2023-06-11 20:08:15 +00:00
.await
2023-06-11 20:08:17 +00:00
.map_err(|err| err.to_string())?;
2023-06-11 20:08:15 +00:00
2023-06-11 20:08:17 +00:00
// Look for SRS info
let mut srs_info_map = HashMap::new();
if opts.include_srs_info {
let looking_for_character_clause = match opts.character {
None => String::new(),
Some(_) => format!("AND AssociatedKanji = ?"),
};
let query_string = format!(
r#"
SELECT ID, NextAnswerDate, AssociatedKanji, CurrentGrade FROM SrsEntrySet
WHERE 1=1
{}
"#,
looking_for_character_clause
);
let mut query = sqlx::query(&query_string);
if let Some(character) = &opts.character {
query = query.bind(character.clone());
}
let result = query
.fetch_all(&srs_db.0)
.await
.map_err(|err| err.to_string())?;
for row in result {
let associated_kanji: String = match row.get("AssociatedKanji") {
Some(v) => v,
None => continue,
};
let id = row.get("ID");
2023-06-11 20:08:19 +00:00
let current_grade = row.get("CurrentGrade");
2023-06-11 20:08:17 +00:00
let next_answer_date: i64 = row.get("NextAnswerDate");
let next_answer_date = Ticks(next_answer_date).epoch_ms();
srs_info_map.insert(
associated_kanji.clone(),
KanjiSrsInfo {
id,
2023-06-11 20:08:19 +00:00
current_grade,
2023-06-11 20:08:17 +00:00
next_answer_date,
associated_kanji,
},
);
}
};
// Put it all together
2023-06-11 20:08:17 +00:00
let kanji = {
let mut new_vec: Vec<Kanji> = Vec::with_capacity(result.len());
let mut last_character: Option<String> = None;
2023-06-11 20:08:15 +00:00
2023-06-11 20:08:17 +00:00
for row in result {
let character: String = row.get("Character");
let most_used_rank = row.get("MostUsedRank");
let meaning = KanjiMeaning {
id: row.get("KanjiMeaningID"),
meaning: row.get("Meaning"),
};
let same_as = match last_character {
Some(ref last_character) if character == *last_character => {
Some(new_vec.last_mut().unwrap())
}
Some(_) => None,
None => None,
};
2023-06-11 20:08:15 +00:00
2023-06-11 20:08:17 +00:00
last_character = Some(character.clone());
2023-06-11 20:08:16 +00:00
2023-06-11 20:08:17 +00:00
if let Some(kanji) = same_as {
kanji.meanings.push(meaning);
} else {
2023-06-11 20:08:17 +00:00
let srs_info = srs_info_map.remove(&character);
2023-06-11 20:08:17 +00:00
new_vec.push(Kanji {
character,
most_used_rank,
meanings: vec![meaning],
2023-06-11 20:08:17 +00:00
srs_info,
2023-06-11 20:08:17 +00:00
});
}
}
new_vec
};
let count = sqlx::query("SELECT COUNT(*) FROM KanjiSet")
2023-06-11 20:08:17 +00:00
.fetch_one(&kanji_db.0)
2023-06-11 20:08:17 +00:00
.await
.map_err(|err| err.to_string())?;
let count = count.try_get(0).map_err(|err| err.to_string())?;
Ok(GetKanjiResult { kanji, count })
2023-06-11 20:08:15 +00:00
}