use std::borrow::Cow;
use std::fs;
use std::path::Path;
use lindera::dictionary::{load_dictionary_with_options, load_fs_dictionary_with_options};
use lindera::mode::Mode;
use lindera::segmenter::Segmenter;
use lindera_dictionary::builder::DictionaryBuilder;
use lindera_dictionary::dictionary::metadata::Metadata;
const CHAR_DEF: &str = "\
DEFAULT 0 1 0
SPACE 0 1 0
KANJI 0 0 2
0x0020 SPACE
0x4E00..0x9FFF KANJI
";
const UNK_DEF: &str = "\
DEFAULT,0,0,10000,補助記号,一般,*,*,*,*,*,*,*
KANJI,0,0,10000,名詞,一般,*,*,*,*,*,*,*
";
const LEX_CSV: &str = "\
東京,0,0,0,名詞,固有名詞,地域,一般,*,*,東京,トウキョウ,トウキョウ
都,0,0,0,名詞,接尾,地域,*,*,*,都,ト,ト
";
const MATRIX_DEF: &str = "\
1 1
0 0 0
";
fn build_dictionary() -> tempfile::TempDir {
let source = tempfile::tempdir().unwrap();
fs::write(source.path().join("char.def"), CHAR_DEF).unwrap();
fs::write(source.path().join("unk.def"), UNK_DEF).unwrap();
fs::write(source.path().join("lex.csv"), LEX_CSV).unwrap();
fs::write(source.path().join("matrix.def"), MATRIX_DEF).unwrap();
let output = tempfile::tempdir().unwrap();
DictionaryBuilder::new(Metadata::default())
.build_dictionary(source.path(), output.path())
.unwrap();
output
}
fn segment(dict_dir: &Path, text: &str, use_mmap: bool) -> Vec<(String, String)> {
let dictionary =
load_fs_dictionary_with_options(dict_dir, use_mmap).expect("dictionary should load");
let segmenter = Segmenter::new(Mode::Normal, dictionary, None);
let mut tokens = segmenter.segment(Cow::Borrowed(text)).unwrap();
tokens
.iter_mut()
.map(|t| {
let pos = t.get_detail(0).unwrap_or("").to_string();
(t.surface.to_string(), pos)
})
.collect()
}
#[test]
fn mmap_loading_matches_plain_loading_via_fs_path() {
let dict = build_dictionary();
let plain = segment(dict.path(), "東京都", false);
let mmapped = segment(dict.path(), "東京都", true);
let expected = vec![
("東京".to_string(), "名詞".to_string()),
("都".to_string(), "名詞".to_string()),
];
assert_eq!(plain, expected);
assert_eq!(mmapped, expected);
}
#[test]
fn mmap_loading_matches_plain_loading_via_uri() {
let dict = build_dictionary();
let uri = dict.path().to_str().unwrap();
let plain = load_dictionary_with_options(uri, false).unwrap();
let mmapped = load_dictionary_with_options(uri, true).unwrap();
let plain_segmenter = Segmenter::new(Mode::Normal, plain, None);
let mmapped_segmenter = Segmenter::new(Mode::Normal, mmapped, None);
let text = "東京都";
let plain_surfaces: Vec<_> = plain_segmenter
.segment(Cow::Borrowed(text))
.unwrap()
.into_iter()
.map(|t| t.surface.to_string())
.collect();
let mmapped_surfaces: Vec<_> = mmapped_segmenter
.segment(Cow::Borrowed(text))
.unwrap()
.into_iter()
.map(|t| t.surface.to_string())
.collect();
assert_eq!(mmapped_surfaces, plain_surfaces);
}
#[test]
#[cfg(feature = "embed-ipadic")]
fn mmap_flag_is_ignored_for_embedded_dictionaries() {
let result = load_dictionary_with_options("embedded://ipadic", true);
assert!(result.is_ok());
}
#[test]
fn default_load_matches_both_explicit_modes() {
use lindera::dictionary::load_fs_dictionary;
let dict = build_dictionary();
let default_dictionary = load_fs_dictionary(dict.path()).expect("default load should succeed");
let segmenter = Segmenter::new(Mode::Normal, default_dictionary, None);
let mut tokens = segmenter.segment(Cow::Borrowed("東京都")).unwrap();
let default_result: Vec<(String, String)> = tokens
.iter_mut()
.map(|t| {
let pos = t.get_detail(0).unwrap_or("").to_string();
(t.surface.to_string(), pos)
})
.collect();
assert_eq!(default_result, segment(dict.path(), "東京都", false));
assert_eq!(default_result, segment(dict.path(), "東京都", true));
}
#[test]
fn from_config_without_use_mmap_defaults_and_segments() {
let dict = build_dictionary();
let config = serde_json::json!({
"dictionary": dict.path().to_str().unwrap(),
"mode": "normal",
});
let segmenter = Segmenter::from_config(&config).expect("from_config should succeed");
let tokens = segmenter.segment(Cow::Borrowed("東京都")).unwrap();
let surfaces: Vec<_> = tokens.into_iter().map(|t| t.surface.to_string()).collect();
assert_eq!(surfaces, vec!["東京".to_string(), "都".to_string()]);
}
#[test]
fn dictionary_clone_shares_all_fields() {
use std::sync::Arc;
let dict_dir = build_dictionary();
let dictionary = load_fs_dictionary_with_options(dict_dir.path(), false).unwrap();
let cloned = dictionary.clone();
assert!(Arc::ptr_eq(
&dictionary.prefix_dictionary,
&cloned.prefix_dictionary
));
assert!(Arc::ptr_eq(
&dictionary.connection_cost_matrix,
&cloned.connection_cost_matrix
));
assert!(Arc::ptr_eq(
&dictionary.character_definition,
&cloned.character_definition
));
assert!(Arc::ptr_eq(
&dictionary.unknown_dictionary,
&cloned.unknown_dictionary
));
assert!(Arc::ptr_eq(&dictionary.metadata, &cloned.metadata));
}