skerry 0.1.7

Super Kool ERRors Yoh - A type-safe, zero-boilerplate error management framework.
Documentation
//! # Skerry: Super Kool ERRors Yoh
//!
//! Example:
//! ```rust
//! use skerry::*;
//!
//! // There can be only one #[sherry_mod] in your project
//! # struct ErrorFromLib;
//! #[skerry_mod]
//! pub mod errors {
//!     pub struct DatabaseError;
//!     pub struct AuthError;
//!     pub struct ValidationError;
//!
//!     pub struct InvalidParse;
//!
//!     #[from]
//!     pub struct LibError(ErrorFromLib);
//! }
//!
//! // Generates a CheckAuthError enum automatically
//! #[skerry_fn]
//! fn check_auth() -> Result<(), e![AuthError]> {
//!     Err(CheckAuthError::AuthError(AuthError))
//! }
//!
//! # fn lib_fn_that_returns_error() -> Result<(), ErrorFromLib> {
//! #   Err(ErrorFromLib)
//! # }
//!
//! struct Controller;
//!
//! #[skerry_impl(prefix(Controller))] // This allows #[skerry_fn] to run on impl blocks
//! impl Controller {
//!     // Use '*' to expand and bubble up sub-errors seamlessly.
//!     #[skerry_fn]
//!     pub fn run() -> Result<(), e![LibError, *CheckAuthError]> {
//!         // AuthError is pulled in from check_auth via '*CheckAuthError'
//!         check_auth()?;
//!
//!         // Automatically bubble up library errors as long as an
//!         // error from `#[skerry_mod]` implements `From` for it.
//!         lib_fn_that_returns_error()?;
//!
//!         Ok(())
//!     }
//! }
//! #[skerry_trait]
//! trait ToJson {
//!     #[skerry_fn]
//!     fn to_json(&self) -> Result<(), e![InvalidParse]>;
//! }
//!
//! #[skerry_impl]
//! impl ToJson for Controller {
//!     // Whenever you do not want to generate a new error just don't
//!     // use e![], this will instead reuse an existing error
//!     #[skerry_fn]
//!     fn to_json(&self) -> Result<(), ToJsonError> {
//!         Ok(())
//!     }
//! }
//!
//! // Manually define composite errors like this
//! define_error!(ManualDefine, [ValidationError, *ToJsonError]);
//! ```
//!
//! ## Core Workflow
//!
//! - Define all possible error structs in a `#[skerry_mod]`.
//! - Mark functions with `#[skerry_fn]`.
//! - Use the `*` operator to bubble up errors from sub-functions without manually mapping variants.
//!
//! ---
//!
//! ## The Error Module
//! Every project needs one module (usually `errors.rs`) that acts as the source of truth.
//!
//! ```rust
//! // Recommended to be pub for easier macro expansions
//! pub use skerry::*;
//! # struct LibError;
//!
//! #[skerry_mod]
//! mod errors {
//!     pub struct ErrA;
//!     pub struct ErrB;
//!     pub struct ErrC;
//!     pub struct DatabaseErr;
//!     #[from]
//!     pub struct OuterLibError(LibError);
//! }
//! ```
//!
//! You can also anotate with `#[from]` to automatically add conversions from the inner type.
//! This is only valid for tuple structs with a single element.
//!
//! *Note: When using errors in any other file, import them via `crate::errors::*;` instead
//! of individual imports to ensure the macros can resolve the paths correctly.*
//!
//! ---
//!
//! ## Function-Specific Enums
//!
//! By using `#[skerry_fn]`, you define a return type using a tuple of error structs.
//! Skerry transforms this into a unique enum named `{FunctionName}Error`.
//!
//! ```rust
//! # pub use skerry::*;
//! # #[skerry_mod]
//! # mod errors {
//! #     pub struct ErrA;
//! #     pub struct ErrB;
//! #     pub struct ErrC;
//! #     pub struct DatabaseErr;
//! # }
//! #[skerry_fn]
//! pub fn low_level() -> Result<(), e![ErrA, ErrB]> {
//!     // Generates LowLevelError { ErrA(ErrA), ErrB(ErrB) }
//!     Err(LowLevelError::ErrA(ErrA))
//!     // You can also type Err(ErrA.into())
//! }
//! ```
//!
//! ---
//!
//! ## The Asterisk (`*`) Expansion
//!
//! When you put `*OtherFnError` in your return array it pulls all
//! variants from `OtherFnError` into your current function's list.
//!
//! * **Deduplication**: Variants are deduplicated automatically. If `ErrA` is added manually
//!   and also exists inside a `*` expansion, only one variant is generated.
//!
//! ```rust
//! # pub use skerry::*;
//! # #[skerry_mod]
//! # mod errors {
//! #     pub struct ErrA;
//! #     pub struct ErrB;
//! #     pub struct ErrC;
//! #     pub struct DatabaseErr;
//! # }
//! # #[skerry_fn]
//! # pub fn low_level() -> Result<(), e![ErrA, ErrB]> {
//! #     // Generates LowLevelError { ErrA(ErrA), ErrB(ErrB) }
//! #     Err(LowLevelError::ErrA(ErrA)) // You can also type Err(ErrA.into())
//! # }
//! #[skerry_fn]
//! pub fn high_level() -> Result<(), e![ErrC, *LowLevelError]> {
//!     // Sees ErrC -> Adds variant
//!     // Sees *LowLevelError -> Inspects LowLevelError, finds (ErrA, ErrB)
//!     // Final HighLevelError contains variants: ErrA, ErrB, ErrC
//!
//!     low_level()?; // Bubbles up automatically
//!     Ok(())
//! }
//! ```
//!
//! The syntax below has the exact same effects, `*LowLevelError` is nothing more than syntatic sugar
//!
//! ```rust
//! # pub use skerry::*;
//! # #[skerry_mod]
//! # mod errors {
//! #     pub struct ErrA;
//! #     pub struct ErrB;
//! #     pub struct ErrC;
//! #     pub struct DatabaseErr;
//! # }
//! # #[skerry_fn]
//! # pub fn low_level() -> Result<(), e![ErrA, ErrB]> {
//! #     // Generates LowLevelError { ErrA(ErrA), ErrB(ErrB) }
//! #     Err(LowLevelError::ErrA(ErrA)) // You can also type Err(ErrA.into())
//! # }
//! #[skerry_fn]
//! pub fn high_level() -> Result<(), e![ErrA, ErrB, ErrC]> {
//!     // ...
//! # Ok(())
//! }
//! ```
//!
//! In the cases above the generated enum looks like this
//! ```rust
//! # struct ErrA;
//! # struct ErrB;
//! # struct ErrC;
//! pub enum HighLevelError {
//!     ErrA(ErrA),
//!     ErrB(ErrB),
//!     ErrC(ErrC),
//! }
//! ```
//! ## Using Skerry inside Impl Blocks
//!
//! Skerry provides the `#[skerry_impl]` attribute to handle methods within `impl` blocks.
//! This attribute coordinates with `#[skerry_fn]` to split the generated code
//! so error enums are generated outside the `impl` block.
//!
//! ### Example
//!
//! ```rust
//! # use skerry::*;
//! # #[skerry_mod]
//! # mod errors {
//! #   pub struct ConnectionFailed;
//! # }
//! # #[skerry_fn]
//! # pub fn remote_call() -> Result<(), e![ConnectionFailed]> {
//! #    Ok(())
//! # }
//! pub struct Database;
//!
//! #[skerry_impl(prefix(Database))] // Optional prefix for functions inside impl block
//! impl Database {
//!     #[skerry_fn]
//!     pub fn connect(&self) -> Result<(), e![*RemoteCallError]> {
//!         remote_call()?;
//!         Ok(())
//!     }
//! }
//!
//! fn main() {
//!     let db = Database;
//!     let result: Result<(), DatabaseConnectError> = db.connect();
//!     assert!(result.is_ok());
//! }
//! ```
//! ## Using Skerry inside Trait Blocks
//!
//! Skerry provides the `#[skerry_trait]` attribute to handle methods within `trait` blocks.
//! This attribute coordinates with `#[skerry_fn]` to split the generated code
//! so error enums are generated outside the `trait` block.
//!
//! ### Example
//!
//! ```rust
//! # use skerry::*;
//! # #[skerry_mod]
//! # mod errors {
//! #   pub struct ParseFailed;
//! # }
//! #[skerry_trait(prefix(ToJson))] // Optional prefix for functions inside trait block
//! trait ToJson {
//!     #[skerry_fn]
//!     fn parse(&self) -> Result<(), e![ParseFailed]>;
//! }
//! ```
//! # Manual Error Definitions
//!
//! Manually define composite errors using the `define_error!` macro.
//! This allows you to skip needing a function to define errors.
//!
//! ### Example
//! ```rust
//! # use skerry::*;
//! # #[skerry_mod]
//! # mod errors {
//! #   pub struct ErrorA;
//! #   pub struct ErrorB;
//! # }
//! define_error!(ManualDefine, [ErrorA, ErrorB]);
//!
//! #[skerry_fn]
//! fn my_func_with_custom_error() -> Result<(), ManualDefine> {
//!     Ok(())
//! }
//! ```
//! # Custom Result Feature
//!
//! The `custom_result` feature implements a specialized result type and leverages the
//! unstable features to enable even more automation.
//!
//! ## Nightly Features Required
//!
//! To use the full suite of automation provided by this feature, you must enable the
//! following nightly features in your crate root:
//!
//! ```rust
//! #![feature(try_trait_v2)]
//! #![feature(custom_inner_attributes)]
//! #![feature(proc_macro_hygiene)]
//! ```
//!
//! ## Overview
//!
//! Enabling `custom_result` changes the behavior of the `?` operator to support
//! automatic conversion into `GlobalErrors<I>`.
//!
//! | Feature Gate | Effect |
//! |--------------|--------|
//! | `try_trait_v2` | Allows using `?` with custom `Result` types; removes the strict requirement for `#[skerry_fn]` on standard functions. |
//! | `custom_inner_attributes`/`proc_macro_hygiene` | Enables the use of `#![skerry]` at the top of a file to annotate all contents automatically. |
//!
//! ## Comparison
//!
//! ### Standard Manual Approach
//! Traditionally, every function and implementation block requires explicit tagging:
//!
//! ```rust,ignore
//! #[skerry_fn]
//! fn check_auth() -> Result<(), e![AuthError]> {
//!     Err(CheckAuthError::AuthError(AuthError))
//! }
//!
//! struct Controller;
//!
//! #[skerry_impl(prefix(Controller))]
//! impl Controller {
//!     #[skerry_fn]
//!     pub fn run() -> Result<(), e![LibError, *CheckAuthError]> {
//!         check_auth()?;
//!         lib_fn_that_returns_error()?;
//!         Ok(())
//!     }
//! }
//! #[skerry_trait(prefix(ToJson))]
//! trait ToJson {
//!     #[skerry_fn]
//!     fn to_json(&self) -> Result<(), e![InvalidParse]>;
//! }
//!
//! impl ToJson for Controller {
//!     #[skerry_fn]
//!     fn to_json(&self) -> Result<(), ToJsonError> {
//!         Ok(())
//!     }
//! }
//! ```
//!
//! ### Automated Approach with `custom_result`
//! By adding `#![skerry]` to the top of your module, the boilerplate is handled
//! automatically.
//!
//! ```rust,ignore
//! #![skerry]
//!
//! fn check_auth() -> Result<(), e![AuthError]> {
//!     Err(CheckAuthError::AuthError(AuthError))
//! }
//!
//! struct Controller;
//!
//! impl Controller {
//!     pub fn run() -> Result<(), e![LibError, *CheckAuthError]> {
//!         check_auth()?;
//!         lib_fn_that_returns_error()?;
//!         Ok(())
//!     }
//! }
//!
//! trait ToJson {
//!     fn to_json(&self) -> Result<(), e![InvalidParse]>;
//! }
//!
//! impl ToJson for Controller {
//!     fn to_json(&self) -> Result<(), ToJsonError> {
//!         Ok(())
//!     }
//! }
//! ```
//! ---
//!
//! ## Compile-Time Safety
//!
//! Skerry uses a custom trait system (`MissingConvert`) to verify error bounds at
//! compile-time. If you try to use `?` on a function whose errors are not represented
//! in your current return tuple, the compiler will refuse to build.
#![cfg_attr(
    feature = "custom_result",
    allow(unused_features),
    feature(try_trait_v2),
    feature(custom_inner_attributes),
    feature(proc_macro_hygiene)
)]

mod helpers;
mod macros;
mod traits;
pub use skerry_macros::{define_error, skerry, skerry_fn, skerry_impl, skerry_mod, skerry_trait};

pub mod skerry_internals {
    pub use crate::{helpers::*, macros::*, traits::*};
    pub use skerry_macros::*;
}

#[cfg(test)]
mod test;