enum-map and more db attach

This commit is contained in:
polv committed 2026-03-08 14:32:32 +07:00
1 parent a38a28a836
commit ec14023c04
6 files changed
+77 -48

No files matched your search

Generated
+21
View File
@@ -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"
+1
View File
@@ -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"
+44 -41
View File
@@ -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<Mutex<Connection>>,
pub(crate) dir: PathBuf,
yomitan_attached: Arc<AtomicBool>,
yomitan_glossary_attached: Arc<AtomicBool>,
is_db_attached: EnumMap<DbChild, Arc<AtomicBool>>,
}
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<EnumMap<DbChild, &str>> = LazyLock::new(|| {
enum_map! {
DbChild::Yomitan => "yomitan.db",
DbChild::YomitanGlossary => "yomitan-glossary.db",
DbChild::Reading => "reading.db"
}
});
pub(crate) const DBSCHEMA: LazyLock<EnumMap<DbChild, &str>> = LazyLock::new(|| {
enum_map! {
DbChild::Yomitan => "yomitan",
DbChild::YomitanGlossary => "glossary",
DbChild::Reading => "reading"
}
});
impl Database {
pub fn new(db_dir: impl AsRef<Path>) -> Result<Self> {
@@ -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<yomitan::YomitanDatabase> {
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(())
+6 -5
View File
@@ -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<Path>) -> Result<Self> {
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"/");
+3
View File
@@ -1,3 +1,6 @@
#[macro_use]
extern crate enum_map;
mod db;
mod error;
mod service;
+2 -2
View File
@@ -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<String> = vec![];
let mut to_be_removed_dicts: Vec<String> = 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)?;