1use std::{
9 fmt::{self, Display},
10 io, result,
11};
12
13use atomic::Atomic;
14use thiserror::Error;
15
16pub use crate::env_level::EnvLevelError;
17use crate::utils::const_assert;
18#[cfg(feature = "multi-thread")]
19use crate::{sink::Task, RecordOwned};
20
21#[derive(Error, Debug)]
23#[non_exhaustive]
24pub enum Error {
25 #[error("format record error: {0}")]
29 FormatRecord(fmt::Error),
30
31 #[error("write record error: {0}")]
36 WriteRecord(io::Error),
37
38 #[error("flush buffer error: {0}")]
42 FlushBuffer(io::Error),
43
44 #[error("create directory error: {0}")]
48 CreateDirectory(io::Error),
49
50 #[error("open file error: {0}")]
54 OpenFile(io::Error),
55
56 #[error("query file metadata error: {0}")]
61 QueryFileMetadata(io::Error),
62
63 #[error("rename file error: {0}")]
67 RenameFile(io::Error),
68
69 #[error("remove file error: {0}")]
73 RemoveFile(io::Error),
74
75 #[error("attempted to convert a string that doesn't match an existing log level: {0}")]
80 ParseLevel(String),
81
82 #[error("invalid argument {0}")]
84 InvalidArgument(#[from] InvalidArgumentError),
85
86 #[cfg(feature = "multi-thread")]
90 #[error("failed to send message to channel: {0}")]
91 SendToChannel(SendToChannelError, SendToChannelErrorDropped),
92
93 #[cfg(feature = "runtime-pattern")]
98 #[error("failed to build pattern at runtime: {0}")]
99 BuildPattern(BuildPatternError),
100
101 #[cfg(feature = "serde")]
105 #[error("failed to serialize log: {0}")]
106 SerializeRecord(io::Error),
107
108 #[error("{0:?}")]
110 Multiple(Vec<Error>),
111
112 #[cfg(test)]
113 #[doc(hidden)]
114 #[error("{0}")]
115 __ForInternalTestsUseOnly(i32),
116}
117
118#[derive(Error, Debug)]
120#[non_exhaustive]
121pub enum InvalidArgumentError {
122 #[error("'logger name': {0}")]
129 LoggerName(#[from] SetLoggerNameError),
130
131 #[error("'rotation policy': {0}")]
137 RotationPolicy(String),
138
139 #[error("'thread pool capacity': {0}")]
141 ThreadPoolCapacity(String),
142}
143
144#[derive(Error, Debug)]
150pub struct SetLoggerNameError {
151 name: String,
152}
153
154impl SetLoggerNameError {
155 #[must_use]
156 pub(crate) fn new(name: impl Into<String>) -> Self {
157 Self { name: name.into() }
158 }
159
160 #[cfg(test)]
161 #[must_use]
162 pub(crate) fn name(&self) -> &str {
163 &self.name
164 }
165}
166
167impl Display for SetLoggerNameError {
168 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169 write!(f, "name '{}' contains disallowed characters", self.name)
170 }
171}
172
173#[cfg(feature = "multi-thread")]
175#[derive(Error, Debug)]
176#[non_exhaustive]
177pub enum SendToChannelError {
178 #[error("the channel is full")]
184 Full,
185
186 #[error("the channel is disconnected")]
188 Disconnected,
189}
190
191#[cfg(feature = "multi-thread")]
195#[derive(Debug)]
196#[non_exhaustive]
197pub enum SendToChannelErrorDropped {
198 Record(Box<RecordOwned>), Flush,
202}
203
204impl Error {
205 pub(crate) fn push_err<T>(result: Result<T>, new: Self) -> Result<T> {
206 match result {
207 Ok(_) => Err(new),
208 Err(Self::Multiple(mut errors)) => {
209 errors.push(new);
210 Err(Self::Multiple(errors))
211 }
212 Err(prev) => Err(Error::Multiple(vec![prev, new])),
213 }
214 }
215
216 pub(crate) fn push_result<T, N>(result: Result<T>, new: Result<N>) -> Result<T> {
217 match new {
218 Ok(_) => result,
219 Err(err) => Self::push_err(result, err),
220 }
221 }
222}
223
224#[cfg(feature = "multi-thread")]
225impl Error {
226 #[must_use]
227 pub(crate) fn from_crossbeam_send(err: crossbeam::channel::SendError<Task>) -> Self {
228 Self::SendToChannel(
229 SendToChannelError::Disconnected,
230 SendToChannelErrorDropped::from_task(err.0),
231 )
232 }
233
234 #[must_use]
235 pub(crate) fn from_crossbeam_try_send(err: crossbeam::channel::TrySendError<Task>) -> Self {
236 use crossbeam::channel::TrySendError;
237
238 let (error, dropped_task) = match err {
239 TrySendError::Full(dropped) => (SendToChannelError::Full, dropped),
240 TrySendError::Disconnected(dropped) => (SendToChannelError::Disconnected, dropped),
241 };
242
243 Self::SendToChannel(error, SendToChannelErrorDropped::from_task(dropped_task))
244 }
245}
246
247#[cfg(feature = "multi-thread")]
248impl SendToChannelErrorDropped {
249 #[must_use]
250 pub(crate) fn from_task(task: Task) -> Self {
251 match task {
252 Task::Log { record, .. } => Self::Record(Box::new(record)),
253 Task::Flush { .. } => Self::Flush,
254 }
255 }
256}
257
258#[cfg(feature = "runtime-pattern")]
260#[derive(Error, Debug)]
261#[error("{0}")]
262pub struct BuildPatternError(pub(crate) spdlog_internal::pattern_parser::Error);
263
264pub type Result<T> = result::Result<T, Error>;
266
267pub type ErrorHandler = fn(Error);
269
270const_assert!(Atomic::<ErrorHandler>::is_lock_free());
271const_assert!(Atomic::<Option<ErrorHandler>>::is_lock_free());
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276
277 #[test]
278 fn push_err() {
279 macro_rules! make_err {
280 ( $($inputs:tt)+ ) => {
281 Error::__ForInternalTestsUseOnly($($inputs)*)
282 };
283 }
284
285 assert!(matches!(
286 Error::push_err(Ok(()), make_err!(1)),
287 Err(make_err!(1))
288 ));
289
290 assert!(matches!(
291 Error::push_err::<()>(Err(make_err!(1)), make_err!(2)),
292 Err(Error::Multiple(v)) if matches!(v[..], [make_err!(1), make_err!(2)])
293 ));
294 }
295}