use std::path::Path;
use pyo3::prelude::*;
use pyo3::types::PyDict;
use lindera_binding_core::{CoreTokenizer, CoreTokenizerBuilder};
use crate::dictionary::{PyDictionary, PyUserDictionary};
use crate::error::to_py_error;
use crate::token::PyToken;
use crate::util::pydict_to_value;
fn filter_args(args: Option<&Bound<'_, PyDict>>) -> PyResult<serde_json::Value> {
match args {
Some(dict) => pydict_to_value(dict),
None => Ok(serde_json::Value::Object(serde_json::Map::new())),
}
}
#[pyclass(name = "TokenizerBuilder")]
pub struct PyTokenizerBuilder {
pub inner: CoreTokenizerBuilder,
}
#[pymethods]
impl PyTokenizerBuilder {
#[new]
#[pyo3(signature = ())]
fn new() -> PyResult<Self> {
let inner = CoreTokenizerBuilder::new().map_err(to_py_error)?;
Ok(Self { inner })
}
#[pyo3(signature = (file_path))]
#[allow(clippy::wrong_self_convention)]
fn from_file(&self, file_path: &str) -> PyResult<Self> {
let inner = CoreTokenizerBuilder::from_file(Path::new(file_path)).map_err(to_py_error)?;
Ok(Self { inner })
}
#[pyo3(signature = (mode))]
fn set_mode<'a>(mut slf: PyRefMut<'a, Self>, mode: &str) -> PyResult<PyRefMut<'a, Self>> {
slf.inner.set_mode(mode).map_err(to_py_error)?;
Ok(slf)
}
#[pyo3(signature = (path))]
fn set_dictionary<'a>(mut slf: PyRefMut<'a, Self>, path: &str) -> PyResult<PyRefMut<'a, Self>> {
slf.inner.set_dictionary(path);
Ok(slf)
}
#[pyo3(signature = (uri))]
fn set_user_dictionary<'a>(
mut slf: PyRefMut<'a, Self>,
uri: &str,
) -> PyResult<PyRefMut<'a, Self>> {
slf.inner.set_user_dictionary(uri);
Ok(slf)
}
#[pyo3(signature = (keep_whitespace))]
fn set_keep_whitespace<'a>(
mut slf: PyRefMut<'a, Self>,
keep_whitespace: bool,
) -> PyResult<PyRefMut<'a, Self>> {
slf.inner.set_keep_whitespace(keep_whitespace);
Ok(slf)
}
#[pyo3(signature = (kind, args=None))]
fn append_character_filter<'a>(
mut slf: PyRefMut<'a, Self>,
kind: &str,
args: Option<&Bound<'_, PyDict>>,
) -> PyResult<PyRefMut<'a, Self>> {
let args = filter_args(args)?;
slf.inner.append_character_filter(kind, &args);
Ok(slf)
}
#[pyo3(signature = (kind, args=None))]
fn append_token_filter<'a>(
mut slf: PyRefMut<'a, Self>,
kind: &str,
args: Option<&Bound<'_, PyDict>>,
) -> PyResult<PyRefMut<'a, Self>> {
let args = filter_args(args)?;
slf.inner.append_token_filter(kind, &args);
Ok(slf)
}
#[pyo3(signature = ())]
fn build(&self) -> PyResult<PyTokenizer> {
let inner = self.inner.build().map_err(to_py_error)?;
Ok(PyTokenizer { inner })
}
}
#[pyclass(name = "Tokenizer")]
pub struct PyTokenizer {
inner: CoreTokenizer,
}
#[pymethods]
impl PyTokenizer {
#[new]
#[pyo3(signature = (dictionary, mode="normal", user_dictionary=None))]
fn new(
dictionary: PyDictionary,
mode: &str,
user_dictionary: Option<PyUserDictionary>,
) -> PyResult<Self> {
let inner =
CoreTokenizer::from_segmenter(mode, dictionary.inner, user_dictionary.map(|d| d.inner))
.map_err(to_py_error)?;
Ok(Self { inner })
}
#[pyo3(signature = (text))]
fn tokenize(&self, text: &str) -> PyResult<Vec<PyToken>> {
let views = self.inner.tokenize(text).map_err(to_py_error)?;
Ok(views.into_iter().map(PyToken::from_view).collect())
}
#[pyo3(signature = (text, n, unique=false, cost_threshold=None))]
fn tokenize_nbest(
&self,
text: &str,
n: usize,
unique: bool,
cost_threshold: Option<i64>,
) -> PyResult<Vec<(Vec<PyToken>, i64)>> {
let results = self
.inner
.tokenize_nbest(text, n, unique, cost_threshold)
.map_err(to_py_error)?;
let py_results: Vec<(Vec<PyToken>, i64)> = results
.into_iter()
.map(|(views, cost)| (views.into_iter().map(PyToken::from_view).collect(), cost))
.collect();
Ok(py_results)
}
}
pub fn register(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
let py = parent_module.py();
let m = PyModule::new(py, "tokenizer")?;
m.add_class::<PyTokenizerBuilder>()?;
m.add_class::<PyTokenizer>()?;
parent_module.add_submodule(&m)?;
Ok(())
}