mirror of
https://github.com/patarapolw/cjdic2.git
synced 2026-11-09 17:39:35 +00:00
enum-map and more db attach
This commit is contained in:
1 parent
a38a28a836
commit
ec14023c04
6 files changed
+77
-48
No files matched your search
Generated
+21
@@ -639,6 +639,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"blake3",
|
"blake3",
|
||||||
|
"enum-map",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -1124,6 +1125,26 @@ version = "1.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
|
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]]
|
[[package]]
|
||||||
name = "enumflags2"
|
name = "enumflags2"
|
||||||
version = "0.7.12"
|
version = "0.7.12"
|
||||||
|
|||||||
@@ -16,3 +16,4 @@ blake3 = "1.8.3"
|
|||||||
anyhow = "1.0.102"
|
anyhow = "1.0.102"
|
||||||
vibrato = "0.5.2"
|
vibrato = "0.5.2"
|
||||||
zstd = "0.13.3"
|
zstd = "0.13.3"
|
||||||
|
enum-map = "2.7.3"
|
||||||
+44
-41
@@ -2,12 +2,13 @@ use std::{
|
|||||||
env::current_dir,
|
env::current_dir,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{
|
sync::{
|
||||||
Arc, Mutex,
|
Arc, LazyLock, Mutex,
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Ok, Result};
|
use anyhow::{Ok, Result};
|
||||||
|
use enum_map::EnumMap;
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
|
|
||||||
mod yomitan;
|
mod yomitan;
|
||||||
@@ -20,18 +21,32 @@ pub use yomitan_writer::{YomitanProgress, YomitanWriter, YomitanZipImportResult,
|
|||||||
pub(crate) struct Database {
|
pub(crate) struct Database {
|
||||||
pub(crate) conn: Arc<Mutex<Connection>>,
|
pub(crate) conn: Arc<Mutex<Connection>>,
|
||||||
pub(crate) dir: PathBuf,
|
pub(crate) dir: PathBuf,
|
||||||
yomitan_attached: Arc<AtomicBool>,
|
is_db_attached: EnumMap<DbChild, Arc<AtomicBool>>,
|
||||||
yomitan_glossary_attached: Arc<AtomicBool>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DbChild {
|
#[derive(Debug, Enum, Clone, Copy)]
|
||||||
|
pub(crate) enum DbChild {
|
||||||
Yomitan,
|
Yomitan,
|
||||||
YomitanGlossary,
|
YomitanGlossary,
|
||||||
|
Reading,
|
||||||
}
|
}
|
||||||
|
|
||||||
const USER_DBFILE: &str = "user.db";
|
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 {
|
impl Database {
|
||||||
pub fn new(db_dir: impl AsRef<Path>) -> Result<Self> {
|
pub fn new(db_dir: impl AsRef<Path>) -> Result<Self> {
|
||||||
@@ -50,37 +65,28 @@ impl Database {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
conn: Arc::new(Mutex::new(conn)),
|
conn: Arc::new(Mutex::new(conn)),
|
||||||
dir,
|
dir,
|
||||||
yomitan_attached: Arc::new(AtomicBool::new(false)),
|
is_db_attached: enum_map! {
|
||||||
yomitan_glossary_attached: Arc::new(AtomicBool::new(false)),
|
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> {
|
pub fn yomitan(&self) -> Result<yomitan::YomitanDatabase> {
|
||||||
self.ensure_db_attached(DbChild::Yomitan)?;
|
self.ensure_db_attached(DbChild::Yomitan)?;
|
||||||
self.ensure_db_attached(DbChild::YomitanGlossary)?;
|
self.ensure_db_attached(DbChild::YomitanGlossary)?;
|
||||||
|
self.ensure_db_attached(DbChild::Reading)?;
|
||||||
Ok(yomitan::YomitanDatabase::new(self.clone()))
|
Ok(yomitan::YomitanDatabase::new(self.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_db_attached(&self, db_child: DbChild) -> Result<()> {
|
fn ensure_db_attached(&self, db_child: DbChild) -> Result<()> {
|
||||||
match db_child {
|
if self.is_db_attached[db_child].load(Ordering::Acquire) {
|
||||||
DbChild::Yomitan => {
|
return Ok(());
|
||||||
if self.yomitan_attached.load(Ordering::Acquire) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DbChild::YomitanGlossary => {
|
|
||||||
if self.yomitan_glossary_attached.load(Ordering::Acquire) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let conn = self.conn.lock().unwrap();
|
let conn = self.conn.lock().unwrap();
|
||||||
|
let is_attached = self.is_db_attached[db_child].load(Ordering::Relaxed);
|
||||||
let is_attached = match db_child {
|
|
||||||
DbChild::Yomitan => self.yomitan_attached.load(Ordering::Relaxed),
|
|
||||||
DbChild::YomitanGlossary => self.yomitan_glossary_attached.load(Ordering::Relaxed),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !is_attached {
|
if !is_attached {
|
||||||
let dir = self.dir.clone();
|
let dir = self.dir.clone();
|
||||||
@@ -90,16 +96,15 @@ impl Database {
|
|||||||
current_dir()?.join(dir)
|
current_dir()?.join(dir)
|
||||||
};
|
};
|
||||||
|
|
||||||
let db_file = match db_child {
|
let mode = match db_child {
|
||||||
DbChild::Yomitan => YOMITAN_DBFILE,
|
DbChild::Yomitan | DbChild::YomitanGlossary => "?mode=ro&immutable=1",
|
||||||
DbChild::YomitanGlossary => YOMITAN_GLOSSARY_DBFILE,
|
_ => "",
|
||||||
};
|
|
||||||
let schema_name = match db_child {
|
|
||||||
DbChild::Yomitan => "yomitan",
|
|
||||||
DbChild::YomitanGlossary => "glossary",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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)]
|
#[cfg(windows)]
|
||||||
let path = if !path.starts_with("/") {
|
let path = if !path.starts_with("/") {
|
||||||
@@ -108,22 +113,20 @@ impl Database {
|
|||||||
path.to_string()
|
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!(
|
conn.execute_batch(&format!(
|
||||||
r"
|
r"
|
||||||
PRAGMA {}.mmap_size = 3000000000; -- 3_000_000_000
|
PRAGMA {}.mmap_size = 3000000000; -- 3_000_000_000
|
||||||
",
|
",
|
||||||
schema_name
|
DBSCHEMA[db_child]
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
match db_child {
|
self.is_db_attached[db_child].store(true, Ordering::Release);
|
||||||
DbChild::Yomitan => self.yomitan_attached.store(true, Ordering::Release),
|
|
||||||
DbChild::YomitanGlossary => self
|
|
||||||
.yomitan_glossary_attached
|
|
||||||
.store(true, Ordering::Release),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use zip::ZipArchive;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CJDicError, Timer,
|
CJDicError, Timer,
|
||||||
db::{YOMITAN_DBFILE, YOMITAN_GLOSSARY_DBFILE},
|
db::{DBFILE, DbChild},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn blake3_hex(s: &str) -> String {
|
fn blake3_hex(s: &str) -> String {
|
||||||
@@ -65,7 +65,7 @@ pub struct YomitanWriter {
|
|||||||
impl YomitanWriter {
|
impl YomitanWriter {
|
||||||
pub fn new(db_dir: impl AsRef<Path>) -> Result<Self> {
|
pub fn new(db_dir: impl AsRef<Path>) -> Result<Self> {
|
||||||
let dir = db_dir.as_ref().to_path_buf();
|
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(
|
conn.execute_batch(
|
||||||
"
|
"
|
||||||
@@ -193,7 +193,7 @@ impl YomitanWriter {
|
|||||||
.query_row("SELECT MAX(id) FROM glossaries", [], |r| r.get::<_, i64>(0))?
|
.query_row("SELECT MAX(id) FROM glossaries", [], |r| r.get::<_, i64>(0))?
|
||||||
as usize;
|
as usize;
|
||||||
|
|
||||||
let message = &format!("Migrate to {}", YOMITAN_GLOSSARY_DBFILE);
|
let message = &format!("Migrate to {}", DBFILE[DbChild::YomitanGlossary]);
|
||||||
progress_callback(YomitanProgress {
|
progress_callback(YomitanProgress {
|
||||||
message: message.to_string(),
|
message: message.to_string(),
|
||||||
current: 0,
|
current: 0,
|
||||||
@@ -255,7 +255,8 @@ impl YomitanWriter {
|
|||||||
steps: total,
|
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(
|
self.conn.execute_batch(
|
||||||
"
|
"
|
||||||
UPDATE glossaries SET content = '[]';
|
UPDATE glossaries SET content = '[]';
|
||||||
@@ -368,7 +369,7 @@ impl YomitanWriter {
|
|||||||
current_dir()?.join(dir)
|
current_dir()?.join(dir)
|
||||||
};
|
};
|
||||||
|
|
||||||
let db_file = YOMITAN_GLOSSARY_DBFILE;
|
let db_file = DBFILE[DbChild::YomitanGlossary];
|
||||||
let schema_name = "glossary";
|
let schema_name = "glossary";
|
||||||
|
|
||||||
let path = path.join(db_file).to_string_lossy().replace(r"\", r"/");
|
let path = path.join(db_file).to_string_lossy().replace(r"\", r"/");
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate enum_map;
|
||||||
|
|
||||||
mod db;
|
mod db;
|
||||||
mod error;
|
mod error;
|
||||||
mod service;
|
mod service;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use vibrato::{Dictionary, Tokenizer};
|
|||||||
use crate::{
|
use crate::{
|
||||||
Timer, ZipSource,
|
Timer, ZipSource,
|
||||||
db::{
|
db::{
|
||||||
Database, YOMITAN_DBFILE, YomitanProgress, YomitanRow, YomitanWriter,
|
DBFILE, Database, DbChild, YomitanProgress, YomitanRow, YomitanWriter,
|
||||||
YomitanZipImportResult,
|
YomitanZipImportResult,
|
||||||
},
|
},
|
||||||
error::CJDicError,
|
error::CJDicError,
|
||||||
@@ -182,7 +182,7 @@ impl AppService {
|
|||||||
let mut new_dicts: Vec<String> = vec![];
|
let mut new_dicts: Vec<String> = vec![];
|
||||||
let mut to_be_removed_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() {
|
if db_path.exists() {
|
||||||
let conn = Connection::open(&db_path)?;
|
let conn = Connection::open(&db_path)?;
|
||||||
|
|||||||
Reference in new issue
Block a user