From a97da3fa1dd8f12ed20a58f26f6a6b5adfb04d4e Mon Sep 17 00:00:00 2001 From: polv Date: Sun, 8 Mar 2026 19:17:31 +0700 Subject: [PATCH] plan search db --- src-core/src/db.rs | 27 +++++++++++++++------------ src-core/src/db/search.rs | 17 +++++++++++++++++ src-core/src/db/yomitan.rs | 4 ++-- src-core/src/db/yomitan_writer.rs | 18 +++++++++++------- src-core/src/error.rs | 16 +++++++++++++++- src-core/src/service.rs | 2 +- 6 files changed, 61 insertions(+), 23 deletions(-) create mode 100644 src-core/src/db/search.rs diff --git a/src-core/src/db.rs b/src-core/src/db.rs index bfb155f..2122ced 100644 --- a/src-core/src/db.rs +++ b/src-core/src/db.rs @@ -7,16 +7,19 @@ use std::{ }, }; -use anyhow::{Ok, Result}; use enum_map::EnumMap; use rusqlite::Connection; +mod search; + mod yomitan; pub use yomitan::YomitanRow; mod yomitan_writer; pub use yomitan_writer::{YomitanProgress, YomitanWriter, YomitanZipImportResult, ZipSource}; +use crate::CJDicError; + #[derive(Clone)] pub(crate) struct Database { pub(crate) conn: Arc>, @@ -28,28 +31,28 @@ pub(crate) struct Database { pub(crate) enum DbChild { Yomitan, YomitanGlossary, - Reading, + Search, } const USER_DBFILE: &str = "user.db"; -pub(crate) const DBFILE: LazyLock> = LazyLock::new(|| { +pub(crate) static DBFILE: LazyLock> = LazyLock::new(|| { enum_map! { DbChild::Yomitan => "yomitan.db", DbChild::YomitanGlossary => "yomitan-glossary.db", - DbChild::Reading => "reading.db" + DbChild::Search => "reading.db" } }); -pub(crate) const DBSCHEMA: LazyLock> = LazyLock::new(|| { +pub(crate) static DBSCHEMA: LazyLock> = LazyLock::new(|| { enum_map! { DbChild::Yomitan => "yomitan", DbChild::YomitanGlossary => "glossary", - DbChild::Reading => "reading" + DbChild::Search => "reading" } }); impl Database { - pub fn new(db_dir: impl AsRef) -> Result { + pub fn new(db_dir: impl AsRef) -> Result { let dir = db_dir.as_ref().to_path_buf(); let conn = Connection::open(dir.join(USER_DBFILE))?; @@ -68,24 +71,24 @@ impl Database { is_db_attached: enum_map! { DbChild::Yomitan => Arc::new(AtomicBool::new(false)), DbChild::YomitanGlossary => Arc::new(AtomicBool::new(false)), - DbChild::Reading => Arc::new(AtomicBool::new(false)), + DbChild::Search => Arc::new(AtomicBool::new(false)), }, }) } - pub fn yomitan(&self) -> Result { + pub fn yomitan(&self) -> Result { self.ensure_db_attached(DbChild::Yomitan)?; self.ensure_db_attached(DbChild::YomitanGlossary)?; - self.ensure_db_attached(DbChild::Reading)?; + self.ensure_db_attached(DbChild::Search)?; Ok(yomitan::YomitanDatabase::new(self.clone())) } - fn ensure_db_attached(&self, db_child: DbChild) -> Result<()> { + fn ensure_db_attached(&self, db_child: DbChild) -> Result<(), CJDicError> { if self.is_db_attached[db_child].load(Ordering::Acquire) { return Ok(()); } - let conn = self.conn.lock().unwrap(); + let conn = self.conn.lock()?; let is_attached = self.is_db_attached[db_child].load(Ordering::Relaxed); if !is_attached { diff --git a/src-core/src/db/search.rs b/src-core/src/db/search.rs new file mode 100644 index 0000000..dddc3fb --- /dev/null +++ b/src-core/src/db/search.rs @@ -0,0 +1,17 @@ +#[allow(unused)] +pub(super) fn normalize_reading(s: &str) -> String { + s.chars() + .map(|c| { + if c >= 'ア' && c <= 'ン' { + // Katakana range + if let Some(x) = char::from_u32(c as u32 - 0x60) { + x // Convert to Hiragana + } else { + c // Ensure it's a valid char + } + } else { + c // Return the character as is if it's not in the range + } + }) + .collect() +} diff --git a/src-core/src/db/yomitan.rs b/src-core/src/db/yomitan.rs index fce1ce9..049d643 100644 --- a/src-core/src/db/yomitan.rs +++ b/src-core/src/db/yomitan.rs @@ -31,7 +31,7 @@ impl YomitanDatabase { } fn decompressor(&self) -> Result, CJDicError> { - let conn = self.db.conn.lock().unwrap(); + let conn = self.db.conn.lock()?; let row = conn.query_row( "SELECT value FROM glossary.meta WHERE key = 'zstd_dict'", @@ -79,7 +79,7 @@ impl YomitanDatabase { let eq1 = if q_term_norm.len() > 0 { "GLOB" } else { "=" }; let eq2 = if q_reading.len() > 0 { "GLOB" } else { "=" }; - let conn = self.db.conn.lock().unwrap(); + let conn = self.db.conn.lock()?; conn.execute_batch( " CREATE TEMP TABLE IF NOT EXISTS yomitan_lookup_keys ( diff --git a/src-core/src/db/yomitan_writer.rs b/src-core/src/db/yomitan_writer.rs index a6985b7..eacfa88 100644 --- a/src-core/src/db/yomitan_writer.rs +++ b/src-core/src/db/yomitan_writer.rs @@ -13,7 +13,7 @@ use zip::ZipArchive; use crate::{ CJDicError, Timer, - db::{DBFILE, DbChild}, + db::{DBFILE, DBSCHEMA, DbChild}, }; fn blake3_hex(s: &str) -> String { @@ -174,7 +174,8 @@ impl YomitanWriter { )?; self.create_materialized_views()?; - self.attach_glossary()?; + self.attach_db(DbChild::YomitanGlossary)?; + self.attach_db(DbChild::Search)?; let schema_version = "2026-03-07"; @@ -361,7 +362,7 @@ impl YomitanWriter { Ok(()) } - fn attach_glossary(&mut self) -> Result<(), CJDicError> { + fn attach_db(&mut self, db_child: DbChild) -> Result<(), CJDicError> { let dir = self.dir.clone(); let path = if self.dir.is_absolute() { dir @@ -369,8 +370,8 @@ impl YomitanWriter { current_dir()?.join(dir) }; - let db_file = DBFILE[DbChild::YomitanGlossary]; - let schema_name = "glossary"; + let db_file = DBFILE[db_child]; + let schema_name = DBSCHEMA[db_child]; let path = path.join(db_file).to_string_lossy().replace(r"\", r"/"); @@ -392,7 +393,10 @@ impl YomitanWriter { schema_name ))?; - self.create_schema_glossary()?; + match db_child { + DbChild::YomitanGlossary => self.create_schema_glossary()?, + _ => (), + } Ok(()) } @@ -529,7 +533,7 @@ impl YomitanWriter { } } - self.attach_glossary()?; + self.attach_db(DbChild::YomitanGlossary)?; { let tx = self.conn.transaction()?; diff --git a/src-core/src/error.rs b/src-core/src/error.rs index 3d949a2..3f4926f 100644 --- a/src-core/src/error.rs +++ b/src-core/src/error.rs @@ -1,5 +1,9 @@ -use std::string::FromUtf8Error; +use std::{ + string::FromUtf8Error, + sync::{MutexGuard, PoisonError}, +}; +use rusqlite::Connection; use serde::Serialize; use thiserror::Error; use vibrato::errors::VibratoError; @@ -32,6 +36,9 @@ pub enum CJDicError { #[error("FromUtf8Error: {0}")] FromUtf8Error(String), + #[error("ConnectionMutexGuardError: {0}")] + ConnectionMutexGuardError(String), + #[error("Not found")] NotFound, @@ -87,3 +94,10 @@ impl From for CJDicError { CJDicError::FromUtf8Error(e.to_string()) } } + +impl From>> for CJDicError { + fn from(e: PoisonError>) -> Self { + eprintln!("{e:#}"); + CJDicError::ConnectionMutexGuardError(e.to_string()) + } +} diff --git a/src-core/src/service.rs b/src-core/src/service.rs index f19de71..754fe80 100644 --- a/src-core/src/service.rs +++ b/src-core/src/service.rs @@ -91,7 +91,7 @@ impl AppService { ) -> Result { let _timer = Timer::new(sql.to_string()); - let conn = self.db.conn.lock().unwrap(); + let conn = self.db.conn.lock()?; let converted_params: Vec<_> = params .into_iter() .map(|p| match p {