commit 4cb572f64c505d3fd5db71f0be42508b7117dac1 Author: Andrey Tkachenko Date: Mon Mar 6 20:53:49 2023 +0400 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bc4f466 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "ctxerr" +version = "0.2.0" +authors = ["Andrey Tkachenko "] +repository = "https://github.com/andreytkachenko/ctxerr.git" +keywords = ["error", "handling", "thiserror", "context", "backtrace"] +categories = ["error-handling"] +description = "Tiny wrapper on thiserror embedding backtrace and location" +license = "MIT OR Apache-2.0" +exclude = [".gitignore", ".cargo/config", ".github/**", "codecov.yml"] +edition = "2021" +resolver = "2" + +[workspace] +members = ["derive"] + +[dependencies] +ctxerr_derive = {version = "0.3.0", path = "derive"} +thiserror = "1.0.39" diff --git a/derive/Cargo.toml b/derive/Cargo.toml new file mode 100644 index 0000000..0cc622e --- /dev/null +++ b/derive/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "ctxerr_derive" +version = "0.3.0" +authors = ["Andrey Tkachenko "] +repository = "https://github.com/andreytkachenko/ctxerr.git" +keywords = ["error", "handling", "thiserror", "context", "backtrace"] +categories = ["error-handling"] +description = "Tiny wrapper on thiserror embedding backtrace and location" +license = "MIT OR Apache-2.0" +exclude = [".gitignore", ".cargo/config", ".github/**", "codecov.yml"] +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1" +quote = "1" +syn = {version = "1", features = ["full"]} diff --git a/derive/src/lib.rs b/derive/src/lib.rs new file mode 100644 index 0000000..5b8757e --- /dev/null +++ b/derive/src/lib.rs @@ -0,0 +1,76 @@ +#![recursion_limit = "128"] +extern crate proc_macro; +extern crate proc_macro2; +use proc_macro2::Ident; +use syn::DeriveInput; + +use quote::quote; + +#[proc_macro_attribute] +pub fn ctxerr( + _metadata: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let ast: DeriveInput = syn::parse(input).unwrap(); + let name = ast.ident.to_string(); + if !name.ends_with("Kind") { + panic!("Ttypename should ends with `Kind`!"); + } + + let name_ident = &ast.ident; + let base_name = Ident::new(&name[0..name.len() - 4], ast.ident.span()); + let tokens = quote! { + #[derive(Debug, ctxerr::thiserror::Error)] + #ast + + #[derive(ctxerr::thiserror::Error)] + pub struct #base_name { + kind: #name_ident, + backtrace: Option, + location: Option<&'static core::panic::Location<'static>>, + } + + impl std::fmt::Debug for #base_name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "{:?}: {}", self.kind, self.kind)?; + if let Some(b) = &self.backtrace { + writeln!(f, "backtrace: \n{}", b)?; + } + + Ok(()) + } + } + + impl std::fmt::Display for #base_name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}: {}", self.kind, self.kind)?; + if let Some(loc) = self.location { + writeln!(f, " file: {}:{}:{}", loc.file(), loc.line(), loc.column())?; + } else { + writeln!(f)?; + } + + if let Some(b) = &self.backtrace { + writeln!(f, "backtrace: \n{}", b)?; + } + + Ok(()) + } + } + + impl> From for #base_name { + #[track_caller] + fn from(val: T) -> Self { + let b = std::backtrace::Backtrace::capture(); + Self{ + kind: val.into(), + backtrace: Some(std::backtrace::Backtrace::capture()), + location: Some(core::panic::Location::caller()), + } + } + } + + }; + + tokens.into() +} diff --git a/examples/demo.rs b/examples/demo.rs new file mode 100644 index 0000000..7d90308 --- /dev/null +++ b/examples/demo.rs @@ -0,0 +1,22 @@ +use ctxerr_derive::ctxerr; + +#[ctxerr] +pub enum ErrorKind { + #[error("Some error {0}")] + Error1(#[from] std::io::Error), +} + +fn operation() -> Result<(), std::io::Error> { + std::fs::File::open("")?; + Ok(()) +} + +fn test() -> Result<(), Error> { + Ok(operation()?) +} + +fn main() { + if let Err(err) = test() { + println!("{}", err); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a7a45cc --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +pub use ctxerr_derive::*; +pub use thiserror;