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}