diff --git a/Cargo.lock b/Cargo.lock index eaedb6a21..2baa09fc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,7 +117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fa490e751f3878eb9accb9f18988eca52c2337ce000a8bf31ef50d4c723ca9e" dependencies = [ "android_log-sys", - "env_logger 0.10.0", + "env_logger", "log", "once_cell", ] @@ -415,15 +415,13 @@ checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" [[package]] name = "bindgen" -version = "0.60.1" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" +checksum = "c6720a8b7b2d39dd533285ed438d458f65b31b5c257e6ac7bb3d7e82844dd722" dependencies = [ "bitflags", "cexpr", "clang-sys", - "clap 3.2.22", - "env_logger 0.9.1", "lazy_static", "lazycell", "log", @@ -433,6 +431,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", + "syn 1.0.102", "which", ] @@ -548,6 +547,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" + [[package]] name = "cbindgen" version = "0.24.3" @@ -1275,19 +1280,6 @@ dependencies = [ "syn 2.0.38", ] -[[package]] -name = "env_logger" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - [[package]] name = "env_logger" version = "0.10.0" @@ -1790,12 +1782,6 @@ dependencies = [ "libm", ] -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "hyper" version = "0.14.23" @@ -2517,8 +2503,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "open_jtalk" version = "0.1.25" -source = "git+https://github.com/VOICEVOX/open_jtalk-rs.git?rev=a16714ce16dec76fd0e3041a7acfa484921db3b5#a16714ce16dec76fd0e3041a7acfa484921db3b5" +source = "git+https://github.com/VOICEVOX/open_jtalk-rs.git?rev=70c76bb54522830e92803038191bf533ba68ce85#70c76bb54522830e92803038191bf533ba68ce85" dependencies = [ + "camino", "open_jtalk-sys", "thiserror", ] @@ -2526,7 +2513,7 @@ dependencies = [ [[package]] name = "open_jtalk-sys" version = "0.16.111" -source = "git+https://github.com/VOICEVOX/open_jtalk-rs.git?rev=a16714ce16dec76fd0e3041a7acfa484921db3b5#a16714ce16dec76fd0e3041a7acfa484921db3b5" +source = "git+https://github.com/VOICEVOX/open_jtalk-rs.git?rev=70c76bb54522830e92803038191bf533ba68ce85#70c76bb54522830e92803038191bf533ba68ce85" dependencies = [ "bindgen", "cmake", @@ -4345,6 +4332,7 @@ version = "0.0.0" dependencies = [ "anyhow", "async_zip", + "camino", "derive-getters", "derive-new", "derive_more", @@ -4389,6 +4377,7 @@ dependencies = [ "anstyle-query", "anyhow", "assert_cmd", + "camino", "chrono", "clap 4.0.10", "colorchoice", @@ -4452,6 +4441,7 @@ dependencies = [ name = "voicevox_core_python_api" version = "0.0.0" dependencies = [ + "camino", "easy-ext", "log", "pyo3", diff --git a/Cargo.toml b/Cargo.toml index acaa1300b..97c39f93b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ async-std = "1.12.0" async_zip = "0.0.11" binstall-tar = "0.4.39" bytes = "1.1.0" +camino = "1.1.6" cbindgen = "0.24.3" chrono = { version = "0.4.26", default-features = false } clap = "4.0.10" @@ -87,7 +88,7 @@ rev = "ebb9dcb9b26ee681889b52b6db3b4f642b04a250" [workspace.dependencies.open_jtalk] git = "https://github.com/VOICEVOX/open_jtalk-rs.git" -rev = "a16714ce16dec76fd0e3041a7acfa484921db3b5" +rev = "70c76bb54522830e92803038191bf533ba68ce85" # FIXME: iOS対応のpull request(https://github.com/wesleywiser/process_path/pull/16)がマージされる見込みが無いため [workspace.dependencies.process_path] diff --git a/crates/voicevox_core/Cargo.toml b/crates/voicevox_core/Cargo.toml index 8d7c70e7e..8b1ab92f5 100644 --- a/crates/voicevox_core/Cargo.toml +++ b/crates/voicevox_core/Cargo.toml @@ -11,6 +11,7 @@ directml = ["onnxruntime/directml"] [dependencies] anyhow.workspace = true async_zip = { workspace = true, features = ["full"] } +camino.workspace = true derive-getters.workspace = true derive-new.workspace = true derive_more.workspace = true diff --git a/crates/voicevox_core/src/__internal/doctest_fixtures.rs b/crates/voicevox_core/src/__internal/doctest_fixtures.rs index a27478d98..426f6cd09 100644 --- a/crates/voicevox_core/src/__internal/doctest_fixtures.rs +++ b/crates/voicevox_core/src/__internal/doctest_fixtures.rs @@ -1,9 +1,9 @@ -use std::path::Path; +use camino::Utf8Path; use crate::{AccelerationMode, InitializeOptions}; pub async fn synthesizer_with_sample_voice_model( - open_jtalk_dic_dir: impl AsRef, + open_jtalk_dic_dir: impl AsRef, ) -> anyhow::Result> { let syntesizer = crate::tokio::Synthesizer::new( crate::tokio::OpenJtalk::new(open_jtalk_dic_dir).await?, diff --git a/crates/voicevox_core/src/engine/open_jtalk.rs b/crates/voicevox_core/src/engine/open_jtalk.rs index 6eee85708..7c272cb74 100644 --- a/crates/voicevox_core/src/engine/open_jtalk.rs +++ b/crates/voicevox_core/src/engine/open_jtalk.rs @@ -15,11 +15,11 @@ pub trait FullcontextExtractor: Clone + Send + Sync + 'static { pub(crate) mod blocking { use std::{ io::Write as _, - path::Path, sync::{Arc, Mutex}, }; - use anyhow::anyhow; + use anyhow::Context as _; + use camino::{Utf8Path, Utf8PathBuf}; use open_jtalk::{mecab_dict_index, text2mecab, JpCommon, ManagedResource, Mecab, Njd}; use tempfile::NamedTempFile; @@ -32,12 +32,8 @@ pub(crate) mod blocking { pub struct OpenJtalk(pub(super) Arc); impl self::OpenJtalk { - pub fn new(open_jtalk_dict_dir: impl AsRef) -> crate::result::Result { - let dict_dir = open_jtalk_dict_dir - .as_ref() - .to_str() - .unwrap_or_else(|| todo!()) // FIXME: `camino::Utf8Path`を要求するようにする - .to_owned(); + pub fn new(open_jtalk_dict_dir: impl AsRef) -> crate::result::Result { + let dict_dir = open_jtalk_dict_dir.as_ref().to_owned(); // FIXME: この`{}`はGitのdiffを抑えるためだけに存在 { @@ -47,11 +43,12 @@ pub(crate) mod blocking { jpcommon: ManagedResource::initialize(), }; - let result = resources.mecab.load(&*dict_dir); - if !result { - // FIXME: 「システム辞書を読もうとしたけど読めなかった」というエラーをちゃんと用意する - return Err(ErrorRepr::NotLoadedOpenjtalkDict.into()); - } + // FIXME: 「システム辞書を読もうとしたけど読めなかった」というエラーをちゃんと用意する + resources + .mecab + .load(&*dict_dir) + .inspect_err(|e| tracing::error!("{e:?}")) + .map_err(|_| ErrorRepr::NotLoadedOpenjtalkDict)?; Ok(Self(Arc::new(Inner { resources: Mutex::new(resources), @@ -124,7 +121,7 @@ pub(crate) mod blocking { pub(super) struct Inner { resources: std::sync::Mutex, - dict_dir: String, // FIXME: `camino::Utf8PathBuf`にする + dict_dir: Utf8PathBuf, } impl Inner { @@ -145,34 +142,37 @@ pub(crate) mod blocking { NamedTempFile::new().map_err(|e| ErrorRepr::UseUserDict(e.into()))?; let temp_dict_path = temp_dict.into_temp_path(); + // FIXME: `.unwrap()`ではなく、エラーとして回収する + let temp_csv_path = Utf8Path::from_path(temp_csv_path.as_ref()).unwrap(); + let temp_dict_path = Utf8Path::from_path(temp_dict_path.as_ref()).unwrap(); + // Mecabでユーザー辞書をコンパイル // TODO: エラー(SEGV)が出るパターンを把握し、それをRust側で防ぐ。 mecab_dict_index(&[ "mecab-dict-index", "-d", - &self.dict_dir, + self.dict_dir.as_ref(), "-u", - temp_dict_path.to_str().unwrap(), + temp_dict_path.as_ref(), "-f", "utf-8", "-t", "utf-8", - temp_csv_path.to_str().unwrap(), + temp_csv_path.as_ref(), "-q", ]); - self.load_with_userdic(Some(temp_dict_path.as_ref())) + self.load_with_userdic(Some(temp_dict_path)) } } - fn load_with_userdic(&self, dict_path: Option<&Path>) -> crate::result::Result<()> { + fn load_with_userdic(&self, dict_path: Option<&Utf8Path>) -> crate::result::Result<()> { let Resources { mecab, .. } = &mut *self.resources.lock().unwrap(); - let result = mecab.load_with_userdic(self.dict_dir.as_ref(), dict_path); - - if !result { - return Err(ErrorRepr::UseUserDict(anyhow!("辞書を読み込めませんでした。")).into()); - } - Ok(()) + mecab + .load_with_userdic(self.dict_dir.as_ref(), dict_path) + .context("辞書を読み込めませんでした。") + .map_err(ErrorRepr::UseUserDict) + .map_err(Into::into) } } @@ -188,7 +188,7 @@ pub(crate) mod blocking { } pub(crate) mod tokio { - use std::path::Path; + use camino::Utf8Path; use super::FullcontextExtractor; @@ -197,7 +197,7 @@ pub(crate) mod tokio { pub struct OpenJtalk(super::blocking::OpenJtalk); impl self::OpenJtalk { - pub async fn new(open_jtalk_dict_dir: impl AsRef) -> crate::result::Result { + pub async fn new(open_jtalk_dict_dir: impl AsRef) -> crate::result::Result { let open_jtalk_dict_dir = open_jtalk_dict_dir.as_ref().to_owned(); let blocking = crate::task::asyncify(|| super::blocking::OpenJtalk::new(open_jtalk_dict_dir)) diff --git a/crates/voicevox_core_c_api/Cargo.toml b/crates/voicevox_core_c_api/Cargo.toml index ee45eee79..3d950f0e8 100644 --- a/crates/voicevox_core_c_api/Cargo.toml +++ b/crates/voicevox_core_c_api/Cargo.toml @@ -18,6 +18,7 @@ directml = ["voicevox_core/directml"] [dependencies] anstream = { workspace = true, default-features = false, features = ["auto"] } anstyle-query.workspace = true +camino.workspace = true chrono = { workspace = true, default-features = false, features = ["clock"] } colorchoice.workspace = true cstr.workspace = true diff --git a/crates/voicevox_core_c_api/src/c_impls.rs b/crates/voicevox_core_c_api/src/c_impls.rs index 0b57db3e5..4e73bf0fb 100644 --- a/crates/voicevox_core_c_api/src/c_impls.rs +++ b/crates/voicevox_core_c_api/src/c_impls.rs @@ -1,11 +1,12 @@ use std::{ffi::CString, path::Path}; +use camino::Utf8Path; use voicevox_core::{InitializeOptions, Result, VoiceModelId}; use crate::{helpers::CApiResult, OpenJtalkRc, VoicevoxSynthesizer, VoicevoxVoiceModel}; impl OpenJtalkRc { - pub(crate) fn new(open_jtalk_dic_dir: impl AsRef) -> Result { + pub(crate) fn new(open_jtalk_dic_dir: impl AsRef) -> Result { Ok(Self { open_jtalk: voicevox_core::blocking::OpenJtalk::new(open_jtalk_dic_dir)?, }) diff --git a/crates/voicevox_core_python_api/Cargo.toml b/crates/voicevox_core_python_api/Cargo.toml index aa40062ed..be3ecbf27 100644 --- a/crates/voicevox_core_python_api/Cargo.toml +++ b/crates/voicevox_core_python_api/Cargo.toml @@ -11,6 +11,7 @@ crate-type = ["cdylib"] directml = ["voicevox_core/directml"] [dependencies] +camino.workspace = true easy-ext.workspace = true log.workspace = true pyo3 = { workspace = true, features = ["abi3-py38", "extension-module"] } diff --git a/crates/voicevox_core_python_api/src/convert.rs b/crates/voicevox_core_python_api/src/convert.rs index 4b908c48b..6544ce26c 100644 --- a/crates/voicevox_core_python_api/src/convert.rs +++ b/crates/voicevox_core_python_api/src/convert.rs @@ -1,5 +1,6 @@ use std::{error::Error as _, future::Future, iter, path::PathBuf}; +use camino::Utf8PathBuf; use easy_ext::ext; use pyo3::{ exceptions::{PyException, PyValueError}, @@ -38,10 +39,12 @@ pub fn from_acceleration_mode(ob: &PyAny) -> PyResult { } } -pub fn from_utf8_path(ob: &PyAny) -> PyResult { +// FIXME: `VoiceModel`や`UserDict`についてはこれではなく、`PathBuf::extract`を直接使うようにする +pub fn from_utf8_path(ob: &PyAny) -> PyResult { PathBuf::extract(ob)? .into_os_string() .into_string() + .map(Utf8PathBuf::from) .map_err(|s| PyValueError::new_err(format!("{s:?} cannot be encoded to UTF-8"))) } diff --git a/crates/voicevox_core_python_api/src/lib.rs b/crates/voicevox_core_python_api/src/lib.rs index 9d36cafcb..4cde8d711 100644 --- a/crates/voicevox_core_python_api/src/lib.rs +++ b/crates/voicevox_core_python_api/src/lib.rs @@ -6,6 +6,7 @@ use self::convert::{ to_py_user_dict_word, to_py_uuid, to_pydantic_dataclass, to_pydantic_voice_model_meta, to_rust_user_dict_word, to_rust_uuid, VoicevoxCoreResultExt as _, }; +use camino::Utf8PathBuf; use easy_ext::ext; use log::debug; use pyo3::{ @@ -115,7 +116,7 @@ impl VoiceModel { #[staticmethod] fn from_path( py: Python<'_>, - #[pyo3(from_py_with = "from_utf8_path")] path: String, + #[pyo3(from_py_with = "from_utf8_path")] path: Utf8PathBuf, ) -> PyResult<&PyAny> { pyo3_asyncio::tokio::future_into_py(py, async move { let model = voicevox_core::tokio::VoiceModel::from_path(path).await; @@ -146,7 +147,7 @@ impl OpenJtalk { #[allow(clippy::new_ret_no_self)] #[staticmethod] fn new( - #[pyo3(from_py_with = "from_utf8_path")] open_jtalk_dict_dir: String, + #[pyo3(from_py_with = "from_utf8_path")] open_jtalk_dict_dir: Utf8PathBuf, py: Python<'_>, ) -> PyResult<&PyAny> { pyo3_asyncio::tokio::future_into_py(py, async move { @@ -637,6 +638,7 @@ impl UserDict { mod blocking { use std::sync::Arc; + use camino::Utf8PathBuf; use pyo3::{ pyclass, pymethods, types::{IntoPyDict as _, PyBytes, PyDict, PyList}, @@ -661,7 +663,7 @@ mod blocking { #[staticmethod] fn from_path( py: Python<'_>, - #[pyo3(from_py_with = "crate::convert::from_utf8_path")] path: String, + #[pyo3(from_py_with = "crate::convert::from_utf8_path")] path: Utf8PathBuf, ) -> PyResult { let model = voicevox_core::blocking::VoiceModel::from_path(path).into_py_result(py)?; Ok(Self { model }) @@ -688,7 +690,7 @@ mod blocking { impl OpenJtalk { #[new] fn new( - #[pyo3(from_py_with = "super::from_utf8_path")] open_jtalk_dict_dir: String, + #[pyo3(from_py_with = "super::from_utf8_path")] open_jtalk_dict_dir: Utf8PathBuf, py: Python<'_>, ) -> PyResult { let open_jtalk =