Skip to content

Commit

Permalink
change: minor changes for UserDict API (VOICEVOX#835)
Browse files Browse the repository at this point in the history
`UserDict`のパブリックAPIについて以下の変更を加える。

1. `load`と`store`が引数に取るファイルパスについて:
    * Rust APIでは`&str`から`impl AsRef<Path>`にする
    * Python APIでは`StrPath`相当にする
    * Java APIでは`java.io.File`と`java.nio.file.Path`のオーバーロードを
        追加する
2. Rust APIの`with_words`から`&mut IndexMap<…>`が得られるようにする

`dict`や`java.util.HashMap`へ変換するAPIの改良はfuture workとする。
  • Loading branch information
qryxip authored Sep 24, 2024
1 parent 9fdb347 commit 8d603d9
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 27 deletions.
28 changes: 14 additions & 14 deletions crates/voicevox_core/src/user_dict/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl<A: Async> Inner<A> {
f(&mut self.words.lock().unwrap_or_else(|e| panic!("{e}")))
}

async fn load(&self, store_path: &str) -> crate::Result<()> {
async fn load(&self, store_path: impl AsRef<Path>) -> crate::Result<()> {
let words = async {
let words = &A::fs_err_read(store_path).await?;
let words = serde_json::from_slice::<IndexMap<_, _>>(words)?;
Expand Down Expand Up @@ -78,7 +78,7 @@ impl<A: Async> Inner<A> {
})
}

async fn save(&self, store_path: &str) -> crate::Result<()> {
async fn save(&self, store_path: impl AsRef<Path>) -> crate::Result<()> {
A::fs_err_write(
store_path,
serde_json::to_vec(&self.words).expect("should not fail"),
Expand Down Expand Up @@ -109,6 +109,8 @@ impl<A: Async> A {
}

pub(crate) mod blocking {
use std::path::Path;

use indexmap::IndexMap;
use uuid::Uuid;

Expand All @@ -122,7 +124,6 @@ pub(crate) mod blocking {
#[derive(Debug, Default)]
pub struct UserDict(Inner<SingleTasked>);

// TODO: 引数の`path`は全部`AsRef<Path>`にする
impl self::UserDict {
/// ユーザー辞書を作成する。
pub fn new() -> Self {
Expand All @@ -133,17 +134,16 @@ pub(crate) mod blocking {
self.0.to_json()
}

// TODO: `&mut IndexMap<_>`を取れるようにする
pub fn with_words<R>(&self, f: impl FnOnce(&IndexMap<Uuid, UserDictWord>) -> R) -> R {
self.0.with_words(|words| f(words))
pub fn with_words<R>(&self, f: impl FnOnce(&mut IndexMap<Uuid, UserDictWord>) -> R) -> R {
self.0.with_words(f)
}

/// ユーザー辞書をファイルから読み込む。
///
/// # Errors
///
/// ファイルが読めなかった、または内容が不正だった場合はエラーを返す。
pub fn load(&self, store_path: &str) -> Result<()> {
pub fn load(&self, store_path: impl AsRef<Path>) -> Result<()> {
self.0.load(store_path).block_on()
}

Expand All @@ -168,7 +168,7 @@ pub(crate) mod blocking {
}

/// ユーザー辞書を保存する。
pub fn save(&self, store_path: &str) -> Result<()> {
pub fn save(&self, store_path: impl AsRef<Path>) -> Result<()> {
self.0.save(store_path).block_on()
}

Expand All @@ -180,6 +180,8 @@ pub(crate) mod blocking {
}

pub(crate) mod nonblocking {
use std::path::Path;

use indexmap::IndexMap;
use uuid::Uuid;

Expand All @@ -200,7 +202,6 @@ pub(crate) mod nonblocking {
#[derive(Debug, Default)]
pub struct UserDict(Inner<BlockingThreadPool>);

// TODO: 引数の`path`は全部`AsRef<Path>`にする
impl self::UserDict {
/// ユーザー辞書を作成する。
pub fn new() -> Self {
Expand All @@ -211,17 +212,16 @@ pub(crate) mod nonblocking {
self.0.to_json()
}

// TODO: `&mut IndexMap<_>`を取れるようにする
pub fn with_words<R>(&self, f: impl FnOnce(&IndexMap<Uuid, UserDictWord>) -> R) -> R {
self.0.with_words(|words| f(words))
pub fn with_words<R>(&self, f: impl FnOnce(&mut IndexMap<Uuid, UserDictWord>) -> R) -> R {
self.0.with_words(f)
}

/// ユーザー辞書をファイルから読み込む。
///
/// # Errors
///
/// ファイルが読めなかった、または内容が不正だった場合はエラーを返す。
pub async fn load(&self, store_path: &str) -> Result<()> {
pub async fn load(&self, store_path: impl AsRef<Path>) -> Result<()> {
self.0.load(store_path).await
}

Expand All @@ -246,7 +246,7 @@ pub(crate) mod nonblocking {
}

/// ユーザー辞書を保存する。
pub async fn save(&self, store_path: &str) -> Result<()> {
pub async fn save(&self, store_path: impl AsRef<Path>) -> Result<()> {
self.0.save(store_path).await
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import jakarta.annotation.Nonnull;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import java.io.File;
import java.nio.file.Path;
import java.util.HashMap;
import jp.hiroshiba.voicevoxcore.exceptions.LoadUserDictException;
import jp.hiroshiba.voicevoxcore.exceptions.SaveUserDictException;
Expand Down Expand Up @@ -70,6 +72,26 @@ public void importDict(UserDict dict) {
rsImportDict(dict);
}

/**
* ユーザー辞書を読み込む。
*
* @param path ユーザー辞書のパス。
* @throws LoadUserDictException ユーザー辞書を読み込めなかった場合。
*/
public void load(Path path) throws LoadUserDictException {
load(path.toString());
}

/**
* ユーザー辞書を読み込む。
*
* @param path ユーザー辞書のパス。
* @throws LoadUserDictException ユーザー辞書を読み込めなかった場合。
*/
public void load(File path) throws LoadUserDictException {
load(path.toString());
}

/**
* ユーザー辞書を読み込む。
*
Expand All @@ -80,6 +102,26 @@ public void load(String path) throws LoadUserDictException {
rsLoad(path);
}

/**
* ユーザー辞書を保存する。
*
* @param path ユーザー辞書のパス。
* @throws SaveUserDictException ユーザー辞書を保存できなかった場合。
*/
public void save(Path path) throws SaveUserDictException {
rsSave(path.toString());
}

/**
* ユーザー辞書を保存する。
*
* @param path ユーザー辞書のパス。
* @throws SaveUserDictException ユーザー辞書を保存できなかった場合。
*/
public void save(File path) throws SaveUserDictException {
rsSave(path.toString());
}

/**
* ユーザー辞書を保存する。
*
Expand Down
4 changes: 2 additions & 2 deletions crates/voicevox_core_java_api/src/user_dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_UserDict_rsLoad<'local>
.clone();

let path = env.get_string(&path)?;
let path = &Cow::from(&path);
let path = &*Cow::from(&path);

internal.load(path)?;

Expand All @@ -144,7 +144,7 @@ unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_UserDict_rsSave<'local>
.clone();

let path = env.get_string(&path)?;
let path = &Cow::from(&path);
let path = &*Cow::from(&path);

internal.save(path)?;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ class UserDict:
"""このオプジェクトの :class:`dict` としての表現。"""
...
def __init__(self) -> None: ...
async def load(self, path: str) -> None:
async def load(self, path: Union[str, PathLike[str]]) -> None:
"""ファイルに保存されたユーザー辞書を読み込む。
Parameters
Expand All @@ -440,7 +440,7 @@ class UserDict:
ユーザー辞書のパス。
"""
...
async def save(self, path: str) -> None:
async def save(self, path: Union[str, PathLike[str]]) -> None:
"""
ユーザー辞書をファイルに保存する。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ class UserDict:
"""このオプジェクトの :class:`dict` としての表現。"""
...
def __init__(self) -> None: ...
def load(self, path: str) -> None:
def load(self, path: Union[str, PathLike[str]]) -> None:
"""ファイルに保存されたユーザー辞書を読み込む。
Parameters
Expand All @@ -435,7 +435,7 @@ class UserDict:
ユーザー辞書のパス。
"""
...
def save(self, path: str) -> None:
def save(self, path: Union[str, PathLike[str]]) -> None:
"""
ユーザー辞書をファイルに保存する。
Expand Down
1 change: 0 additions & 1 deletion crates/voicevox_core_python_api/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ pub(crate) fn from_acceleration_mode(ob: &PyAny) -> PyResult<AccelerationMode> {
}
}

// FIXME: `UserDict`についてはこれではなく、`PathBuf::extract`を直接使うようにする
pub(crate) fn from_utf8_path(ob: &PyAny) -> PyResult<Utf8PathBuf> {
PathBuf::extract(ob)?
.into_os_string()
Expand Down
10 changes: 4 additions & 6 deletions crates/voicevox_core_python_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -734,11 +734,11 @@ mod blocking {
Self::default()
}

fn load(&self, path: &str, py: Python<'_>) -> PyResult<()> {
fn load(&self, path: PathBuf, py: Python<'_>) -> PyResult<()> {
self.dict.load(path).into_py_result(py)
}

fn save(&self, path: &str, py: Python<'_>) -> PyResult<()> {
fn save(&self, path: PathBuf, py: Python<'_>) -> PyResult<()> {
self.dict.save(path).into_py_result(py)
}

Expand Down Expand Up @@ -1363,19 +1363,17 @@ mod asyncio {
Self::default()
}

fn load<'py>(&self, path: &str, py: Python<'py>) -> PyResult<&'py PyAny> {
fn load<'py>(&self, path: PathBuf, py: Python<'py>) -> PyResult<&'py PyAny> {
let this = self.dict.clone();
let path = path.to_owned();

pyo3_asyncio::tokio::future_into_py(py, async move {
let result = this.load(&path).await;
Python::with_gil(|py| result.into_py_result(py))
})
}

fn save<'py>(&self, path: &str, py: Python<'py>) -> PyResult<&'py PyAny> {
fn save<'py>(&self, path: PathBuf, py: Python<'py>) -> PyResult<&'py PyAny> {
let this = self.dict.clone();
let path = path.to_owned();

pyo3_asyncio::tokio::future_into_py(py, async move {
let result = this.save(&path).await;
Expand Down

0 comments on commit 8d603d9

Please sign in to comment.