use proc_macro::TokenStream;

mod error;

#[proc_macro_derive(RichError, attributes(rich_error))]
/// Derive a rich, location-aware outer error type for an error enum.
///
/// This derive is intended for "inner" error enums that typically already
/// implement [`std::error::Error`] (for example via `thiserror::Error`):
///
/// ```rust,ignore
/// use thiserror::Error;
/// use rich_error::RichError;
///
/// #[derive(Debug, Error, RichError)]
/// pub enum InnerError {
///     #[error("database error: {0}")]
///     Database(database::Error),                      // tuple variant
///
///     #[error("invalid operation from {operation}")]
///     InvalidOperation { operation: &'static str },   // struct variant
///
///     #[error("simple unit error")]
///     SimpleUnit,                                     // unit variant
/// }
/// ```
///
/// For such an enum, `#[derive(RichError)]` will generate, **in the same module**:
///
/// - An outer wrapper type:
///
///   ```rust,ignore
///   #[derive(Debug)]
///   pub struct Error {
///       inner: InnerError,
///       location: &'static std::panic::Location<'static>,
///   }
///   ```
///
/// - A [`Display`](std::fmt::Display) implementation that includes both the
///   inner error message and the call site:
///
///   ```text
///   <inner error display> (at path/to/file.rs:LINE)
///   ```
///
/// - An [`std::error::Error`] implementation that delegates `source()` to
///   the inner enum (so error chaining continues to work as expected).
///
/// - A constructor and accessors:
///
///   ```rust,ignore
///   impl Error {
///       /// Construct from an `InnerError`, capturing the call site.
///       #[track_caller]
///       pub fn new(inner: InnerError) -> Self { /* ... */ }
///
///       /// Access the inner error value.
///       pub fn inner(&self) -> &InnerError { /* ... */ }
///
///       /// Access the captured creation location.
///       pub fn location(&self) -> &'static std::panic::Location<'static> { /* ... */ }
///   }
///   ```
///
/// - **Per-variant convenience constructors** on the outer `Error`:
///
///   - For struct variants:
///
///     ```rust,ignore
///     // InvalidOperation { operation: &'static str }
///     #[track_caller]
///     pub fn invalid_operation(operation: &'static str) -> Error { /* ... */ }
///     ```
///
///   - For tuple variants:
///
///     ```rust,ignore
///     // InnerError::Database(database::Error)
///     #[track_caller]
///     pub fn database(arg0: database::Error) -> Error { /* ... */ }
///     ```
///
///   - For unit variants:
///
///     ```rust,ignore
///     // InnerError::SimpleUnit
///     #[track_caller]
///     pub fn simple_unit() -> Error { /* ... */ }
///     ```
///
///   All of these constructors call `Error::new(..)` and therefore capture
///   the **caller’s** file and line via `#[track_caller]`.
///
/// - A `From<InnerError> for Error` implementation:
///
///   ```rust,ignore
///   impl From<InnerError> for Error {
///       #[track_caller]
///       fn from(inner: InnerError) -> Self {
///           Error::new(inner)
///       }
///   }
///   ```
///
///   This allows you to write functions that return `Result<T, Error>` and
///   still use `?` on `Result<T, InnerError>`:
///
///   ```rust,ignore
///   fn do_work() -> Result<(), Error> {
///       some_inner_fallible_call()?; // Result<_, InnerError>
///       Ok(())
///   }
///   ```
///
/// - For **single-field variants** (either struct or tuple), an additional
///   `From<FieldType> for Error` implementation is generated. For example:
///
///   ```rust,ignore
///   #[derive(Debug, Error, RichError)]
///   pub enum InnerError {
///       #[error("database error: {0}")]
///       Database(database::Error),
///   }
///   ```
///
///   expands to:
///
///   ```rust,ignore
///   impl From<database::Error> for Error {
///       #[track_caller]
///       fn from(e: database::Error) -> Self {
///           Error::database(e)
///       }
///   }
///   ```
///
///   This means you can write:
///
///   ```rust,ignore
///   async fn handler(db: &Db) -> Result<(), Error> {
///       // `begin()` returns Result<_, database::Error>
///       let tx = db.begin().await?; // `?` converts database::Error → Error
///       Ok(())
///   }
///   ```
///
/// # Custom outer type names
///
/// By default the generated wrapper is called `Error`, but you can choose a name
/// that better matches the module by adding the helper attribute
/// `#[rich_error(outer = MyError)]`:
///
/// ```rust,ignore
/// #[derive(Debug, Error, RichError)]
/// #[rich_error(outer = BridgeError)]
/// pub enum BridgeInner {
///     #[error("bridge error: {0}")]
///     Tuple(sqlx::Error),
/// }
///
/// pub type Result<T> = std::result::Result<T, BridgeError>;
/// ```
///
/// # Why capture locations?
///
/// In many real systems, errors are created deep in the stack (e.g. in a
/// repository or client) but are only logged much later in a handler or
/// outer service. By the time you see the log, you often no longer know
/// **where** the error was originally created.
///
/// `RichError` uses `#[track_caller]` + [`std::panic::Location::caller`]
/// to record the call site every time an `Error` is constructed (either via
/// `Error::new`, a generated constructor like `Error::database`, or a
/// `From<..>` conversion used by `?`). This makes logs far more actionable:
///
/// ```text
/// database error: connection refused (at crates/my-crate/src/db.rs:42)
/// ```
///
/// instead of just the error message alone.
///
/// # Supported enum shapes
///
/// `RichError` supports:
///
/// - struct variants: `Variant { field: Ty, .. }`
/// - tuple variants:  `Variant(Ty0, Ty1, ..)`
/// - unit variants:   `Variant`
///
/// For struct and tuple variants with exactly **one** field, `From<FieldTy>
/// for Error` is also generated as described above.
///
/// # Requirements
///
/// - `RichError` must be derived on an **enum**; deriving it on structs or
///   other items will produce a compile error.
/// - The inner enum should typically also derive or implement
///   [`std::error::Error`] (for example via `thiserror::Error`) so that
///   the outer `Error` can delegate its `source()` method correctly.
///
/// # Caveats
///
/// - The outer error type defaults to `Error` and is generated in the same module
///   as the enum on which `RichError` is derived. Override with
///   `#[rich_error(outer = MyError)]` if you need a different name.
/// - Because `#[track_caller]` records the immediate caller, passing
///   constructors as bare function pointers into combinators like
///   [`Result::map_err`] (e.g. `map_err(Error::database)`) will record a
///   call site inside the standard library. Prefer using `?` with the
///   generated `From<..>` impls, or closures like `map_err(|e| Error::database(e))`
///   if you need combinators.
///
pub fn rich_error(input: TokenStream) -> TokenStream {
    error::rich_error_impl(input)
}
