diff --git a/Cargo.lock b/Cargo.lock index 426060c..7c054bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -639,6 +639,7 @@ version = "0.1.0" dependencies = [ "anyhow", "blake3", + "enum-map", "rusqlite", "serde", "serde_json", @@ -1124,6 +1125,26 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "enumflags2" version = "0.7.12" diff --git a/src-core/Cargo.toml b/src-core/Cargo.toml index de0d015..c3e6689 100644 --- a/src-core/Cargo.toml +++ b/src-core/Cargo.toml @@ -16,3 +16,4 @@ blake3 = "1.8.3" anyhow = "1.0.102" vibrato = "0.5.2" zstd = "0.13.3" +enum-map = "2.7.3" diff --git a/src-core/src/db.rs b/src-core/src/db.rs index 7c57b52..bfb155f 100644 --- a/src-core/src/db.rs +++ b/src-core/src/db.rs @@ -2,12 +2,13 @@ use std::{ env::current_dir, path::{Path, PathBuf}, sync::{ - Arc, Mutex, + Arc, LazyLock, Mutex, atomic::{AtomicBool, Ordering}, }, }; use anyhow::{Ok, Result}; +use enum_map::EnumMap; use rusqlite::Connection; mod yomitan; @@ -20,18 +21,32 @@ pub use yomitan_writer::{YomitanProgress, YomitanWriter, YomitanZipImportResult, pub(crate) struct Database { pub(crate) conn: Arc>, pub(crate) dir: PathBuf, - yomitan_attached: Arc, - yomitan_glossary_attached: Arc, + is_db_attached: EnumMap>, } -enum DbChild { +#[derive(Debug, Enum, Clone, Copy)] +pub(crate) enum DbChild { Yomitan, YomitanGlossary, + Reading, } const USER_DBFILE: &str = "user.db"; -pub(crate) const YOMITAN_DBFILE: &str = "yomitan.db"; -pub(crate) const YOMITAN_GLOSSARY_DBFILE: &str = "yomitan-glossary.db"; + +pub(crate) const DBFILE: LazyLock> = LazyLock::new(|| { + enum_map! { + DbChild::Yomitan => "yomitan.db", + DbChild::YomitanGlossary => "yomitan-glossary.db", + DbChild::Reading => "reading.db" + } +}); +pub(crate) const DBSCHEMA: LazyLock> = LazyLock::new(|| { + enum_map! { + DbChild::Yomitan => "yomitan", + DbChild::YomitanGlossary => "glossary", + DbChild::Reading => "reading" + } +}); impl Database { pub fn new(db_dir: impl AsRef) -> Result { @@ -50,37 +65,28 @@ impl Database { Ok(Self { conn: Arc::new(Mutex::new(conn)), dir, - yomitan_attached: Arc::new(AtomicBool::new(false)), - yomitan_glossary_attached: Arc::new(AtomicBool::new(false)), + 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)), + }, }) } pub fn yomitan(&self) -> Result { self.ensure_db_attached(DbChild::Yomitan)?; self.ensure_db_attached(DbChild::YomitanGlossary)?; + self.ensure_db_attached(DbChild::Reading)?; Ok(yomitan::YomitanDatabase::new(self.clone())) } fn ensure_db_attached(&self, db_child: DbChild) -> Result<()> { - match db_child { - DbChild::Yomitan => { - if self.yomitan_attached.load(Ordering::Acquire) { - return Ok(()); - } - } - DbChild::YomitanGlossary => { - if self.yomitan_glossary_attached.load(Ordering::Acquire) { - return Ok(()); - } - } + if self.is_db_attached[db_child].load(Ordering::Acquire) { + return Ok(()); } let conn = self.conn.lock().unwrap(); - - let is_attached = match db_child { - DbChild::Yomitan => self.yomitan_attached.load(Ordering::Relaxed), - DbChild::YomitanGlossary => self.yomitan_glossary_attached.load(Ordering::Relaxed), - }; + let is_attached = self.is_db_attached[db_child].load(Ordering::Relaxed); if !is_attached { let dir = self.dir.clone(); @@ -90,16 +96,15 @@ impl Database { current_dir()?.join(dir) }; - let db_file = match db_child { - DbChild::Yomitan => YOMITAN_DBFILE, - DbChild::YomitanGlossary => YOMITAN_GLOSSARY_DBFILE, - }; - let schema_name = match db_child { - DbChild::Yomitan => "yomitan", - DbChild::YomitanGlossary => "glossary", + let mode = match db_child { + DbChild::Yomitan | DbChild::YomitanGlossary => "?mode=ro&immutable=1", + _ => "", }; - let path = path.join(db_file).to_string_lossy().replace(r"\", r"/"); + let path = path + .join(DBFILE[db_child]) + .to_string_lossy() + .replace(r"\", r"/"); #[cfg(windows)] let path = if !path.starts_with("/") { @@ -108,22 +113,20 @@ impl Database { path.to_string() }; - let uri = format!("file:{}?mode=ro&immutable=1", path); + let uri = format!("file:{}{}", path, mode); - conn.execute(&format!("ATTACH DATABASE ?1 AS {}", schema_name), [uri])?; + conn.execute( + &format!("ATTACH DATABASE ?1 AS {}", DBSCHEMA[db_child]), + [uri], + )?; conn.execute_batch(&format!( r" PRAGMA {}.mmap_size = 3000000000; -- 3_000_000_000 ", - schema_name + DBSCHEMA[db_child] ))?; - match db_child { - DbChild::Yomitan => self.yomitan_attached.store(true, Ordering::Release), - DbChild::YomitanGlossary => self - .yomitan_glossary_attached - .store(true, Ordering::Release), - }; + self.is_db_attached[db_child].store(true, Ordering::Release); } Ok(()) diff --git a/src-core/src/db/yomitan_writer.rs b/src-core/src/db/yomitan_writer.rs index 4efffb5..a6985b7 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::{YOMITAN_DBFILE, YOMITAN_GLOSSARY_DBFILE}, + db::{DBFILE, DbChild}, }; fn blake3_hex(s: &str) -> String { @@ -65,7 +65,7 @@ pub struct YomitanWriter { impl YomitanWriter { pub fn new(db_dir: impl AsRef) -> Result { let dir = db_dir.as_ref().to_path_buf(); - let conn = Connection::open(dir.join(YOMITAN_DBFILE))?; + let conn = Connection::open(dir.join(DBFILE[DbChild::Yomitan]))?; conn.execute_batch( " @@ -193,7 +193,7 @@ impl YomitanWriter { .query_row("SELECT MAX(id) FROM glossaries", [], |r| r.get::<_, i64>(0))? as usize; - let message = &format!("Migrate to {}", YOMITAN_GLOSSARY_DBFILE); + let message = &format!("Migrate to {}", DBFILE[DbChild::YomitanGlossary]); progress_callback(YomitanProgress { message: message.to_string(), current: 0, @@ -255,7 +255,8 @@ impl YomitanWriter { steps: total, }); - let _timer = Timer::new(format!("cleanup for {}", YOMITAN_GLOSSARY_DBFILE)); + let _timer = + Timer::new(format!("cleanup for {}", DBFILE[DbChild::YomitanGlossary])); self.conn.execute_batch( " UPDATE glossaries SET content = '[]'; @@ -368,7 +369,7 @@ impl YomitanWriter { current_dir()?.join(dir) }; - let db_file = YOMITAN_GLOSSARY_DBFILE; + let db_file = DBFILE[DbChild::YomitanGlossary]; let schema_name = "glossary"; let path = path.join(db_file).to_string_lossy().replace(r"\", r"/"); diff --git a/src-core/src/lib.rs b/src-core/src/lib.rs index 5714329..fcd366d 100644 --- a/src-core/src/lib.rs +++ b/src-core/src/lib.rs @@ -1,3 +1,6 @@ +#[macro_use] +extern crate enum_map; + mod db; mod error; mod service; diff --git a/src-core/src/service.rs b/src-core/src/service.rs index 7b6ead4..f19de71 100644 --- a/src-core/src/service.rs +++ b/src-core/src/service.rs @@ -11,7 +11,7 @@ use vibrato::{Dictionary, Tokenizer}; use crate::{ Timer, ZipSource, db::{ - Database, YOMITAN_DBFILE, YomitanProgress, YomitanRow, YomitanWriter, + DBFILE, Database, DbChild, YomitanProgress, YomitanRow, YomitanWriter, YomitanZipImportResult, }, error::CJDicError, @@ -182,7 +182,7 @@ impl AppService { let mut new_dicts: Vec = vec![]; let mut to_be_removed_dicts: Vec = vec![]; - let db_path = self.db.dir.join(YOMITAN_DBFILE); + let db_path = self.db.dir.join(DBFILE[DbChild::Yomitan]); if db_path.exists() { let conn = Connection::open(&db_path)?;