use std::{
fmt::{self, Display},
io, result,
};
use atomic::Atomic;
use static_assertions::const_assert;
use thiserror::Error;
pub use crate::env_level::EnvLevelError;
#[cfg(feature = "multi-thread")]
use crate::{sink::Task, RecordOwned};
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum Error {
#[error("format record error: {0}")]
FormatRecord(fmt::Error),
#[error("write record error: {0}")]
WriteRecord(io::Error),
#[error("flush buffer error: {0}")]
FlushBuffer(io::Error),
#[error("create directory error: {0}")]
CreateDirectory(io::Error),
#[error("open file error: {0}")]
OpenFile(io::Error),
#[error("query file metadata error: {0}")]
QueryFileMetadata(io::Error),
#[error("rename file error: {0}")]
RenameFile(io::Error),
#[error("remove file error: {0}")]
RemoveFile(io::Error),
#[error("attempted to convert a string that doesn't match an existing log level: {0}")]
ParseLevel(String),
#[error("invalid argument {0}")]
InvalidArgument(#[from] InvalidArgumentError),
#[cfg(feature = "multi-thread")]
#[error("failed to send message to channel: {0}")]
SendToChannel(SendToChannelError, SendToChannelErrorDropped),
}
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum InvalidArgumentError {
#[error("'logger name': {0}")]
LoggerName(#[from] SetLoggerNameError),
#[error("'rotation policy': {0}")]
RotationPolicy(String),
#[error("'thread pool capacity': {0}")]
ThreadPoolCapacity(String),
}
#[derive(Error, Debug)]
pub struct SetLoggerNameError {
name: String,
}
impl SetLoggerNameError {
#[must_use]
pub(crate) fn new(name: impl Into<String>) -> Self {
Self { name: name.into() }
}
#[cfg(test)]
#[must_use]
pub(crate) fn name(&self) -> &str {
&self.name
}
}
impl Display for SetLoggerNameError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "name '{}' contains disallowed characters", self.name)
}
}
#[cfg(feature = "multi-thread")]
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum SendToChannelError {
#[error("the channel is full")]
Full,
#[error("the channel is disconnected")]
Disconnected,
}
#[cfg(feature = "multi-thread")]
#[derive(Debug)]
#[non_exhaustive]
pub enum SendToChannelErrorDropped {
Record(RecordOwned),
Flush,
}
#[cfg(feature = "multi-thread")]
impl Error {
#[must_use]
pub(crate) fn from_crossbeam_send(err: crossbeam::channel::SendError<Task>) -> Self {
Self::SendToChannel(
SendToChannelError::Disconnected,
SendToChannelErrorDropped::from_task(err.0),
)
}
#[must_use]
pub(crate) fn from_crossbeam_try_send(err: crossbeam::channel::TrySendError<Task>) -> Self {
use crossbeam::channel::TrySendError;
let (error, dropped_task) = match err {
TrySendError::Full(dropped) => (SendToChannelError::Full, dropped),
TrySendError::Disconnected(dropped) => (SendToChannelError::Disconnected, dropped),
};
Self::SendToChannel(error, SendToChannelErrorDropped::from_task(dropped_task))
}
}
#[cfg(feature = "multi-thread")]
impl SendToChannelErrorDropped {
#[must_use]
pub(crate) fn from_task(task: Task) -> Self {
match task {
Task::Log { record, .. } => Self::Record(record),
Task::Flush { .. } => Self::Flush,
}
}
}
pub type Result<T> = result::Result<T, Error>;
pub type ErrorHandler = fn(Error);
const_assert!(Atomic::<ErrorHandler>::is_lock_free());
const_assert!(Atomic::<Option<ErrorHandler>>::is_lock_free());