use std::io;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error("Parse error at line {line}, column {column}: {message}")]
Parse {
message: String,
line: usize,
column: usize,
file: Option<String>,
},
#[error("Unknown format: {format}")]
UnknownFormat {
format: String,
},
#[error("Key '{key}' not found")]
KeyNotFound {
key: String,
available: Vec<String>,
},
#[error("Type error: cannot convert '{value}' to {expected_type}")]
Type {
value: String,
expected_type: String,
actual_type: String,
},
#[error("File error for '{path}': {source}")]
Io {
path: String,
#[source]
source: io::Error,
},
#[cfg(feature = "schema")]
#[error("Schema error at '{path}': {message}")]
Schema {
path: String,
message: String,
expected: Option<String>,
},
#[error("Validation error: {message}")]
Validation {
message: String,
},
#[error("{message}")]
General {
message: String,
},
#[error("Feature '{feature}' is not enabled. Enable with features = [\"{feature}\"]")]
FeatureNotEnabled {
feature: String,
},
#[error("Concurrency error: {message}")]
Concurrency {
message: String,
},
#[cfg(feature = "noml")]
#[error("NOML error: {source}")]
Noml {
#[from]
source: noml::NomlError,
},
#[error("Internal error: {message}")]
Internal {
message: String,
context: Option<String>,
},
}
impl Error {
pub fn parse(message: impl Into<String>, line: usize, column: usize) -> Self {
Self::Parse {
message: message.into(),
line,
column,
file: None,
}
}
pub fn parse_with_file(
message: impl Into<String>,
line: usize,
column: usize,
file: impl Into<String>,
) -> Self {
Self::Parse {
message: message.into(),
line,
column,
file: Some(file.into()),
}
}
pub fn key_not_found(key: impl Into<String>) -> Self {
Self::KeyNotFound {
key: key.into(),
available: Vec::new(),
}
}
pub fn key_not_found_with_suggestions(key: impl Into<String>, available: Vec<String>) -> Self {
Self::KeyNotFound {
key: key.into(),
available,
}
}
pub fn type_error(
value: impl Into<String>,
expected: impl Into<String>,
actual: impl Into<String>,
) -> Self {
Self::Type {
value: value.into(),
expected_type: expected.into(),
actual_type: actual.into(),
}
}
pub fn io(path: impl Into<String>, source: io::Error) -> Self {
Self::Io {
path: path.into(),
source,
}
}
pub fn unknown_format(format: impl Into<String>) -> Self {
Self::UnknownFormat {
format: format.into(),
}
}
pub fn feature_not_enabled(feature: impl Into<String>) -> Self {
Self::FeatureNotEnabled {
feature: feature.into(),
}
}
pub fn concurrency(message: impl Into<String>) -> Self {
Self::Concurrency {
message: message.into(),
}
}
pub fn serialize(message: impl Into<String>) -> Self {
Self::General {
message: message.into(),
}
}
#[cfg(feature = "schema")]
pub fn schema(path: impl Into<String>, message: impl Into<String>) -> Self {
Self::Schema {
path: path.into(),
message: message.into(),
expected: None,
}
}
#[cfg(feature = "schema")]
pub fn schema_with_expected(
path: impl Into<String>,
message: impl Into<String>,
expected: impl Into<String>,
) -> Self {
Self::Schema {
path: path.into(),
message: message.into(),
expected: Some(expected.into()),
}
}
pub fn validation(message: impl Into<String>) -> Self {
Self::Validation {
message: message.into(),
}
}
pub fn general(message: impl Into<String>) -> Self {
Self::General {
message: message.into(),
}
}
pub fn internal(message: impl Into<String>) -> Self {
Self::Internal {
message: message.into(),
context: None,
}
}
pub fn internal_with_context(message: impl Into<String>, context: impl Into<String>) -> Self {
Self::Internal {
message: message.into(),
context: Some(context.into()),
}
}
}
impl From<io::Error> for Error {
fn from(source: io::Error) -> Self {
Self::Io {
path: "unknown".to_string(),
source,
}
}
}