Skip to main content

sudachi/
error.rs

1/*
2 * Copyright (c) 2021-2026 Works Applications Co., Ltd.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use std::fmt::Debug;
18use thiserror::Error;
19
20use crate::config::ConfigError;
21use crate::dic::build::error::DicBuildError;
22use crate::dic::character_category::Error as CharacterCategoryError;
23use crate::dic::description::DescriptionError;
24use crate::dic::error::DictionaryCompatibilityError;
25use crate::dic::header::HeaderError;
26use crate::dic::lexicon_set::LexiconSetError;
27use crate::dic::read::error::SudachiNomError;
28use crate::dic::word_id::WordId;
29use crate::dic::word_info::WordInfoError;
30use crate::plugin::PluginError;
31
32pub type SudachiResult<T> = Result<T, SudachiError>;
33
34/// Sudachi error
35#[derive(Error, Debug)]
36#[non_exhaustive]
37pub enum SudachiError {
38    #[error("{context}: {cause}")]
39    ErrWithContext {
40        context: String,
41        cause: Box<SudachiError>,
42    },
43
44    #[error("{context}: {cause}")]
45    Io {
46        cause: std::io::Error,
47        context: String,
48    },
49
50    #[error("Parse Int Error")]
51    ParseIntError(#[from] std::num::ParseIntError),
52
53    #[error("Invalid UTF-16: {0}")]
54    FromUtf16(#[from] std::string::FromUtf16Error),
55
56    #[error("Regex error")]
57    RegexError { cause: Box<fancy_regex::Error> },
58
59    #[error("Error from nom {0}")]
60    NomParseError(String),
61
62    #[error("Invalid utf16 string from nom")]
63    InvalidUtf16FromNom,
64
65    #[error("Serde error: {0}")]
66    SerdeError(#[from] serde_json::Error),
67
68    #[error("Invalid character category definition: {0}")]
69    InvalidCharacterCategory(#[from] CharacterCategoryError),
70
71    #[error("Config Error: {0}")]
72    ConfigError(#[from] ConfigError),
73
74    #[error("Invalid header: {0}")]
75    InvalidHeader(#[from] HeaderError),
76
77    #[error("Invalid description: {0}")]
78    InvalidDescription(#[from] DescriptionError),
79
80    #[error("Lexicon error")]
81    LexiconSetError(#[from] LexiconSetError),
82
83    #[error("Plugin error")]
84    PluginError(#[from] PluginError),
85
86    #[error("End of sentence (EOS) is not connected to beginning of sentence (BOS)")]
87    EosBosDisconnect,
88
89    #[error("Invalid character category type: {0}")]
90    InvalidCharacterCategoryType(String),
91
92    #[error("Invalid data format: {1} at line {0}")]
93    InvalidDataFormat(usize, String),
94
95    #[error(transparent)]
96    WordInfo(#[from] WordInfoError),
97
98    #[error("Invalid grammar")]
99    InvalidDictionaryGrammar,
100
101    #[error("Connection matrix is missing")]
102    ConnectionMatrixMissing,
103
104    #[error("Invalid part of speech: {0}")]
105    InvalidPartOfSpeech(String),
106
107    #[error("Invalid word id: {0}")]
108    InvalidWordId(WordId),
109
110    #[error("Invalid range: {0}..{1}")]
111    InvalidRange(usize, usize),
112
113    #[error("No out of vocabulary plugin provided")]
114    NoOOVPluginProvided,
115
116    #[error(transparent)]
117    DictionaryCompatibility(#[from] DictionaryCompatibilityError),
118
119    #[error("Input is too long, it can't be more than {1} bytes, was {0}")]
120    InputTooLong(usize, usize),
121
122    #[error(transparent)]
123    DictionaryCompilationError(#[from] DicBuildError),
124
125    #[error("MorphemeList is borrowed, make sure that all Ref<> are dropped")]
126    MorphemeListBorrowed,
127}
128
129impl From<std::io::Error> for SudachiError {
130    fn from(e: std::io::Error) -> Self {
131        SudachiError::Io {
132            cause: e,
133            context: String::from("IO Error"),
134        }
135    }
136}
137
138impl From<fancy_regex::Error> for SudachiError {
139    fn from(e: fancy_regex::Error) -> Self {
140        SudachiError::RegexError { cause: Box::new(e) }
141    }
142}
143
144impl SudachiError {
145    pub fn with_context<S: Into<String>>(self, ctx: S) -> Self {
146        match self {
147            SudachiError::Io { cause, .. } => SudachiError::Io {
148                cause,
149                context: ctx.into(),
150            },
151            cause => SudachiError::ErrWithContext {
152                cause: Box::new(cause),
153                context: ctx.into(),
154            },
155        }
156    }
157}
158
159impl<I: Debug> From<nom::Err<SudachiNomError<I>>> for SudachiError {
160    fn from(err: nom::Err<SudachiNomError<I>>) -> Self {
161        if let nom::Err::Failure(SudachiNomError::Utf16String) = err {
162            return SudachiError::InvalidUtf16FromNom;
163        }
164        SudachiError::NomParseError(format!("{}", err))
165    }
166}