use thiserror::Error;
#[derive(Error, Debug)]
pub enum RuitlError {
#[error("Template error: {message}")]
Template { message: String },
#[error("Component error: {message}")]
Component { message: String },
#[error("Validation error: {message}")]
Validation { message: String },
#[error("Render error: {message}")]
Render { message: String },
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Configuration error: {message}")]
Config { message: String },
#[error("Build error: {message}")]
Build { message: String },
#[error("Server error: {message}")]
Server { message: String },
#[error("Route error: {message}")]
Route { message: String },
#[error("Static generation error: {message}")]
StaticGen { message: String },
#[error("Serialization error: {0}")]
Serde(#[from] serde_json::Error),
#[error("TOML error: {0}")]
Toml(#[from] toml::de::Error),
#[error("Parse error: {message}")]
Parse { message: String },
#[error("Code generation error: {message}")]
Codegen { message: String },
#[error("HTTP error: {0}")]
Http(#[from] hyper::Error),
#[error("File system error: {0}")]
WalkDir(#[from] walkdir::Error),
#[error("Address parsing error: {0}")]
AddrParse(#[from] std::net::AddrParseError),
#[error("HTTP error: {0}")]
HttpError(#[from] hyper::http::Error),
#[error("Error: {message}")]
Generic { message: String },
}
impl From<ruitl_compiler::CompileError> for RuitlError {
fn from(err: ruitl_compiler::CompileError) -> Self {
use ruitl_compiler::CompileError;
match err {
CompileError::Parse { message } => Self::Parse { message },
CompileError::Codegen { message } => Self::Codegen { message },
CompileError::Io(e) => Self::Io(e),
CompileError::WalkDir(e) => Self::WalkDir(e),
}
}
}
impl RuitlError {
pub fn template<S: Into<String>>(message: S) -> Self {
Self::Template {
message: message.into(),
}
}
pub fn component<S: Into<String>>(message: S) -> Self {
Self::Component {
message: message.into(),
}
}
pub fn validation<S: Into<String>>(message: S) -> Self {
Self::Validation {
message: message.into(),
}
}
pub fn render<S: Into<String>>(message: S) -> Self {
Self::Render {
message: message.into(),
}
}
pub fn config<S: Into<String>>(message: S) -> Self {
Self::Config {
message: message.into(),
}
}
pub fn build<S: Into<String>>(message: S) -> Self {
Self::Build {
message: message.into(),
}
}
pub fn server<S: Into<String>>(message: S) -> Self {
Self::Server {
message: message.into(),
}
}
pub fn route<S: Into<String>>(message: S) -> Self {
Self::Route {
message: message.into(),
}
}
pub fn parse<S: Into<String>>(message: S) -> Self {
Self::Parse {
message: message.into(),
}
}
pub fn codegen<S: Into<String>>(message: S) -> Self {
Self::Codegen {
message: message.into(),
}
}
pub fn static_gen<S: Into<String>>(message: S) -> Self {
Self::StaticGen {
message: message.into(),
}
}
pub fn generic<S: Into<String>>(message: S) -> Self {
Self::Generic {
message: message.into(),
}
}
pub fn message(&self) -> String {
self.to_string()
}
pub fn is_template(&self) -> bool {
matches!(self, Self::Template { .. })
}
pub fn is_component(&self) -> bool {
matches!(self, Self::Component { .. })
}
pub fn is_validation(&self) -> bool {
matches!(self, Self::Validation { .. })
}
pub fn is_render(&self) -> bool {
matches!(self, Self::Render { .. })
}
pub fn is_io(&self) -> bool {
matches!(self, Self::Io(_))
}
pub fn is_config(&self) -> bool {
matches!(self, Self::Config { .. })
}
pub fn is_build(&self) -> bool {
matches!(self, Self::Build { .. })
}
pub fn is_server(&self) -> bool {
matches!(self, Self::Server { .. })
}
}
pub type Result<T> = std::result::Result<T, RuitlError>;
pub trait ResultExt<T> {
fn template_context<S: Into<String>>(self, context: S) -> Result<T>;
fn component_context<S: Into<String>>(self, context: S) -> Result<T>;
fn render_context<S: Into<String>>(self, context: S) -> Result<T>;
fn config_context<S: Into<String>>(self, context: S) -> Result<T>;
fn build_context<S: Into<String>>(self, context: S) -> Result<T>;
fn server_context<S: Into<String>>(self, context: S) -> Result<T>;
fn static_gen_context<S: Into<String>>(self, context: S) -> Result<T>;
}
impl<T, E> ResultExt<T> for std::result::Result<T, E>
where
E: Into<RuitlError>,
{
fn template_context<S: Into<String>>(self, context: S) -> Result<T> {
self.map_err(|e| {
let original = e.into();
RuitlError::template(format!("{}: {}", context.into(), original.message()))
})
}
fn component_context<S: Into<String>>(self, context: S) -> Result<T> {
self.map_err(|e| {
let original = e.into();
RuitlError::component(format!("{}: {}", context.into(), original.message()))
})
}
fn render_context<S: Into<String>>(self, context: S) -> Result<T> {
self.map_err(|e| {
let original = e.into();
RuitlError::render(format!("{}: {}", context.into(), original.message()))
})
}
fn config_context<S: Into<String>>(self, context: S) -> Result<T> {
self.map_err(|e| {
let original = e.into();
RuitlError::config(format!("{}: {}", context.into(), original.message()))
})
}
fn build_context<S: Into<String>>(self, context: S) -> Result<T> {
self.map_err(|e| {
let original = e.into();
RuitlError::build(format!("{}: {}", context.into(), original.message()))
})
}
fn server_context<S: Into<String>>(self, context: S) -> Result<T> {
self.map_err(|e| {
let original = e.into();
RuitlError::server(format!("{}: {}", context.into(), original.message()))
})
}
fn static_gen_context<S: Into<String>>(self, context: S) -> Result<T> {
self.map_err(|e| {
let original = e.into();
RuitlError::static_gen(format!("{}: {}", context.into(), original.message()))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_creation() {
let err = RuitlError::template("test template error");
assert!(err.is_template());
assert!(!err.is_component());
assert!(err.message().contains("test template error"));
}
#[test]
fn test_result_ext() {
let result: std::result::Result<(), std::io::Error> = Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"file not found",
));
let with_context = result.template_context("Failed to load template");
assert!(with_context.is_err());
let err = with_context.unwrap_err();
assert!(err.is_template());
assert!(err.message().contains("Failed to load template"));
}
#[test]
fn test_error_types() {
assert!(RuitlError::component("test").is_component());
assert!(RuitlError::render("test").is_render());
assert!(RuitlError::config("test").is_config());
assert!(RuitlError::build("test").is_build());
assert!(RuitlError::server("test").is_server());
assert!(RuitlError::generic("test").message().contains("test"));
}
}