use thiserror::Error;
#[derive(Error, Debug, Clone)]
#[error("Argument error: {message}")]
pub struct ArgumentError {
pub message: String,
}
impl ArgumentError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
}
#[derive(Error, Debug, Clone)]
#[error("Validation error for field '{field}': {message}{}", value.as_ref().map(|v| format!(" (value: '{v}')")).unwrap_or_default())]
pub struct ValidationError {
field: String,
message: String,
value: Option<String>,
}
impl ValidationError {
pub fn new(field: impl Into<String>, message: impl Into<String>) -> Self {
Self {
field: field.into(),
message: message.into(),
value: None,
}
}
pub fn with_value(
field: impl Into<String>,
message: impl Into<String>,
value: impl Into<String>,
) -> Self {
Self {
field: field.into(),
message: message.into(),
value: Some(value.into()),
}
}
pub fn field(&self) -> &str {
&self.field
}
pub fn message(&self) -> &str {
&self.message
}
pub fn value(&self) -> Option<&str> {
self.value.as_deref()
}
}
#[derive(Error, Debug, Clone)]
#[error("Configuration error for key '{key}': {message}")]
pub struct ConfigError {
key: String,
message: String,
}
impl ConfigError {
pub fn new(key: impl Into<String>, message: impl Into<String>) -> Self {
Self {
key: key.into(),
message: message.into(),
}
}
pub fn key(&self) -> &str {
&self.key
}
pub fn message(&self) -> &str {
&self.message
}
}
#[derive(Error, Debug, Clone)]
#[error("Network error in '{operation}'{}: {message}", status_code.map(|s| format!(" (status: {s})")).unwrap_or_default())]
pub struct NetworkError {
operation: String,
message: String,
status_code: Option<u16>,
}
impl NetworkError {
pub fn new(operation: impl Into<String>, message: impl Into<String>) -> Self {
Self {
operation: operation.into(),
message: message.into(),
status_code: None,
}
}
pub fn with_status(
operation: impl Into<String>,
message: impl Into<String>,
status_code: u16,
) -> Self {
Self {
operation: operation.into(),
message: message.into(),
status_code: Some(status_code),
}
}
pub fn operation(&self) -> &str {
&self.operation
}
pub fn message(&self) -> &str {
&self.message
}
pub fn status_code(&self) -> Option<u16> {
self.status_code
}
}
#[derive(Error, Debug, Clone)]
#[error("Parse error{}: expected '{expected}', got '{input}'", position.map(|p| format!(" at position {p}")).unwrap_or_default())]
pub struct ParseError {
input: String,
expected: String,
position: Option<usize>,
}
impl ParseError {
pub fn new(input: impl Into<String>, expected: impl Into<String>) -> Self {
Self {
input: input.into(),
expected: expected.into(),
position: None,
}
}
pub fn with_position(
input: impl Into<String>,
expected: impl Into<String>,
position: usize,
) -> Self {
Self {
input: input.into(),
expected: expected.into(),
position: Some(position),
}
}
pub fn input(&self) -> &str {
&self.input
}
pub fn expected(&self) -> &str {
&self.expected
}
pub fn position(&self) -> Option<usize> {
self.position
}
}
#[derive(Error, Debug)]
pub enum UtilsError {
#[error(transparent)]
Argument(#[from] ArgumentError),
#[error(transparent)]
Validation(#[from] ValidationError),
#[error(transparent)]
Config(#[from] ConfigError),
#[error(transparent)]
Network(#[from] NetworkError),
#[error(transparent)]
Parse(#[from] ParseError),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Error: {0}")]
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}
pub type UtilsResult<T> = Result<T, UtilsError>;
pub fn argument_error(message: impl Into<String>) -> ArgumentError {
ArgumentError::new(message)
}
pub fn validation_error(field: impl Into<String>, message: impl Into<String>) -> ValidationError {
ValidationError::new(field, message)
}
pub fn config_error(key: impl Into<String>, message: impl Into<String>) -> ConfigError {
ConfigError::new(key, message)
}
pub fn network_error(operation: impl Into<String>, message: impl Into<String>) -> NetworkError {
NetworkError::new(operation, message)
}
pub fn parse_error(input: impl Into<String>, expected: impl Into<String>) -> ParseError {
ParseError::new(input, expected)
}