Skip to main content

mecab_sys/
lib.rs

1//! `mecab-sys` is the FFI wrapper for [MeCab](https://taku910.github.io/mecab/).
2//! While [`ffi`] provides the FFI bindings, the top-level items are the safe wrapper for MeCab.
3//!
4//! # Examples
5//!
6//! ```
7//! # fn analyze() {
8//! use mecab_sys::Model;
9//!
10//! let model = Model::from_cli_arg(c"-d /path/to/your/dict -r /path/to/dictrc").unwrap();
11//!
12//! let tagger = model.new_tagger().unwrap();
13//! let mut lattice = model.new_lattice().unwrap();
14//!
15//! let mut lattice = lattice.set_sentence("すもももももももものうち");
16//! tagger.parse(&mut lattice).unwrap();
17//!
18//! for node in lattice.bos_node() {
19//!     let surface = node.surface();
20//!     let feat = node.feature();
21//!
22//!     println!("{surface}: {feat}");
23//! }
24//! # }
25//! ```
26//!
27//! ## Cursor API
28//!
29//! [`lattice.bos_node()`](`LatticeGuard::bos_node()`) returns a [`NodeCursor`], which can be
30//! used either as an iterator and as a cursor of [`Node`]s.
31//!
32//! ```
33//! # fn analyze() {
34//! use mecab_sys::Model;
35//!
36//! let model = Model::from_cli_arg(c"-d /path/to/your/dict -r /path/to/dictrc").unwrap();
37//!
38//! let tagger = model.new_tagger().unwrap();
39//! let mut lattice = model.new_lattice().unwrap();
40//!
41//! let mut lattice = lattice.set_sentence("すもももももももものうち");
42//! tagger.parse(&mut lattice).unwrap();
43//!
44//! let mut cursor = lattice.bos_node();
45//! assert!(cursor.curr().is_some_and(|node| node.kind().is_bos()));
46//!
47//! cursor.move_next();
48//! cursor.move_next();
49//! if let Some(node) = cursor.curr() {
50//!     let surface = node.surface();
51//!     let feat = node.feature();
52//!
53//!     println!("{surface}: {feat}");
54//! }
55//!
56//! cursor.move_prev();
57//! if let Some(node) = cursor.curr() {
58//!     let surface = node.surface();
59//!     let feat = node.feature();
60//!
61//!     println!("{surface}: {feat}");
62//! }
63//! # }
64//! ```
65//!
66//! After you call `move_next()` on the EoS node, call `move_prev()` on the BoS node, or consume as
67//! an iterator, then the cursor shifted to the "dead" state, never being back to the alive state.
68//!
69//! ```
70//! # use mecab_sys::LatticeGuard;
71//! # fn analyze(lattice: &LatticeGuard<'_, '_, '_>) {
72//! let mut cursor = lattice.bos_node();
73//! while cursor.curr().is_some_and(|node| !node.kind().is_eos()) {
74//!     cursor.move_next();
75//! }
76//! assert!(cursor.curr().is_some_and(|node| node.kind().is_eos()));
77//!
78//! // Call `move_next()` on the EoS node
79//! cursor.move_next();
80//! assert!(cursor.curr().is_none());
81//!
82//! // Never back to the original node
83//! cursor.move_prev();
84//! assert!(cursor.curr().is_none());
85//!
86//! let mut cursor = lattice.bos_node();
87//! assert!(cursor.curr().is_some_and(|node| node.kind().is_bos()));
88//!
89//! // Call `move_prev()` on the BoS node
90//! cursor.move_prev();
91//! assert!(cursor.curr().is_none());
92//!
93//! // Never back to the original node
94//! cursor.move_next();
95//! assert!(cursor.curr().is_none());
96//!
97//! let mut cursor = lattice.bos_node();
98//! // Consume the iterator, reaching at the EoS node
99//! for _ in &mut cursor {}
100//! assert!(cursor.curr().is_none());
101//!
102//! // Never back to alive nodes
103//! cursor.move_prev();
104//! assert!(cursor.curr().is_none());
105//! # }
106//! ```
107
108/// The raw FFI layer for MeCab, generated from `mecab.h`.
109pub mod ffi;
110
111#[cfg(test)]
112mod tests;
113
114mod model;
115pub use model::Model;
116
117mod tagger;
118pub use tagger::{BoundaryConstraintType, RequestType};
119pub use tagger::{Lattice, LatticeGuard, Tagger};
120
121mod node;
122pub use node::{LcAttr, RcAttr};
123pub use node::{Node, NodeCursor, NodeKind};
124
125pub use errors::Error;
126mod errors {
127    use super::Lattice;
128    use super::ffi;
129
130    use std::ffi::CStr;
131    use std::fmt;
132
133    fn get_mecab_error(mecab: *mut ffi::mecab_t) -> String {
134        unsafe {
135            let err_ptr = ffi::mecab_strerror(mecab);
136            if err_ptr.is_null() {
137                return "unknown MeCab error".to_string();
138            }
139            CStr::from_ptr(err_ptr).to_string_lossy().into_owned()
140        }
141    }
142
143    fn get_lattice_error(lattice: *mut ffi::mecab_lattice_t) -> String {
144        unsafe {
145            let err_ptr = ffi::mecab_lattice_strerror(lattice);
146            if err_ptr.is_null() {
147                return "unknown MeCab lattice error".to_string();
148            }
149            CStr::from_ptr(err_ptr).to_string_lossy().into_owned()
150        }
151    }
152
153    fn get_global_error() -> String {
154        get_mecab_error(std::ptr::null_mut())
155    }
156
157    /// Represents an error returned by MeCab.
158    #[derive(Debug, Clone, PartialEq, Eq)]
159    pub struct Error(String);
160
161    impl Error {
162        pub(super) fn global() -> Self {
163            Self(get_global_error())
164        }
165
166        pub(super) fn with_lattice(lattice: &Lattice) -> Self {
167            Self(get_lattice_error(lattice.as_ptr()))
168        }
169
170        /// Returns the underlying error message.
171        pub fn into_inner(self) -> String {
172            self.0
173        }
174
175        /// Returns the reference to the underlying error message.
176        pub fn to_inner(&self) -> &str {
177            &self.0
178        }
179    }
180
181    impl fmt::Display for Error {
182        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183            write!(f, "{}", self.0)
184        }
185    }
186    impl std::error::Error for Error {}
187}