Skip to main content

lindera_nodejs/
dictionary.rs

1//! Dictionary management for morphological analysis.
2//!
3//! This module provides functionality for building, loading, and managing dictionaries
4//! used in morphological analysis.
5
6use std::path::Path;
7
8use lindera::dictionary::{
9    Dictionary, DictionaryBuilder, Metadata, UserDictionary,
10    load_dictionary as lindera_load_dictionary,
11    load_user_dictionary as lindera_load_user_dictionary,
12};
13
14use crate::error::to_napi_error;
15use crate::metadata::JsMetadata;
16
17/// A morphological analysis dictionary.
18///
19/// Contains the data structures needed for tokenization and morphological analysis.
20#[napi(js_name = "Dictionary")]
21pub struct JsDictionary {
22    pub(crate) inner: Dictionary,
23}
24
25#[napi]
26impl JsDictionary {
27    /// Returns the name of the dictionary metadata.
28    #[napi]
29    pub fn metadata_name(&self) -> String {
30        self.inner.metadata.name.clone()
31    }
32
33    /// Returns the character encoding of the dictionary.
34    #[napi]
35    pub fn metadata_encoding(&self) -> String {
36        self.inner.metadata.encoding.clone()
37    }
38
39    /// Returns the full metadata object of the dictionary.
40    #[napi]
41    pub fn metadata(&self) -> JsMetadata {
42        JsMetadata::from(self.inner.metadata.clone())
43    }
44}
45
46/// A user-defined dictionary for custom words.
47///
48/// User dictionaries allow you to add custom words and their morphological features
49/// that are not present in the main dictionary.
50#[napi(js_name = "UserDictionary")]
51pub struct JsUserDictionary {
52    pub(crate) inner: UserDictionary,
53}
54
55/// Builds a dictionary from source files.
56///
57/// # Arguments
58///
59/// * `input_dir` - Directory containing dictionary source files.
60/// * `output_dir` - Directory where the built dictionary will be saved.
61/// * `metadata` - Metadata configuration for the dictionary.
62#[napi]
63pub fn build_dictionary(
64    input_dir: String,
65    output_dir: String,
66    metadata: &JsMetadata,
67) -> napi::Result<()> {
68    let input_path = Path::new(&input_dir);
69    let output_path = Path::new(&output_dir);
70
71    if !input_path.exists() {
72        return Err(napi::Error::new(
73            napi::Status::InvalidArg,
74            format!("Input directory does not exist: {input_dir}"),
75        ));
76    }
77
78    let meta: Metadata = JsMetadata::to_lindera_metadata(metadata);
79    let builder = DictionaryBuilder::new(meta);
80
81    builder
82        .build_dictionary(input_path, output_path)
83        .map_err(|e| to_napi_error(format!("Failed to build dictionary: {e}")))?;
84
85    Ok(())
86}
87
88/// Builds a user dictionary from a CSV file.
89///
90/// # Arguments
91///
92/// * `kind` - Dictionary kind (reserved for future use).
93/// * `input_file` - Path to the CSV file containing user dictionary entries.
94/// * `output_dir` - Directory where the built user dictionary will be saved.
95/// * `metadata` - Optional metadata configuration. If omitted, default values are used.
96#[napi]
97pub fn build_user_dictionary(
98    _kind: String,
99    input_file: String,
100    output_dir: String,
101    metadata: Option<&JsMetadata>,
102) -> napi::Result<()> {
103    let input_path = Path::new(&input_file);
104    let output_path = Path::new(&output_dir);
105
106    if !input_path.exists() {
107        return Err(napi::Error::new(
108            napi::Status::InvalidArg,
109            format!("Input file does not exist: {input_file}"),
110        ));
111    }
112
113    let meta = match metadata {
114        Some(m) => JsMetadata::to_lindera_metadata(m),
115        None => Metadata::default(),
116    };
117
118    let builder = DictionaryBuilder::new(meta);
119
120    builder
121        .build_user_dictionary(input_path, output_path)
122        .map_err(|e| to_napi_error(format!("Failed to build user dictionary: {e}")))?;
123
124    Ok(())
125}
126
127/// Loads a dictionary from the specified URI.
128///
129/// # Arguments
130///
131/// * `uri` - URI to the dictionary. Can be a file path or embedded dictionary URI
132///   (e.g. "embedded://ipadic").
133///
134/// # Returns
135///
136/// A loaded Dictionary object.
137#[napi]
138pub fn load_dictionary(uri: String) -> napi::Result<JsDictionary> {
139    lindera_load_dictionary(&uri)
140        .map_err(|e| to_napi_error(format!("Failed to load dictionary from '{uri}': {e}")))
141        .map(|inner| JsDictionary { inner })
142}
143
144/// Loads a user dictionary from the specified URI.
145///
146/// # Arguments
147///
148/// * `uri` - URI to the user dictionary directory.
149/// * `metadata` - Metadata configuration for the user dictionary.
150///
151/// # Returns
152///
153/// A loaded UserDictionary object.
154#[napi]
155pub fn load_user_dictionary(uri: String, metadata: &JsMetadata) -> napi::Result<JsUserDictionary> {
156    let meta: Metadata = JsMetadata::to_lindera_metadata(metadata);
157    lindera_load_user_dictionary(&uri, &meta)
158        .map_err(|e| to_napi_error(format!("Failed to load user dictionary from '{uri}': {e}")))
159        .map(|inner| JsUserDictionary { inner })
160}