wrap-match 1.0.7

A procedural macro to wrap a function in match statement to make error logging super easy.
Documentation
# The Problem

If you ever want to log when an error occurs and what caused it, you may find yourself using a `match` statement for every possible error instead of using the `?` operator.

This results in extremely verbose code. It's a pain to write and maintain.

# Introducing wrap-match!

wrap-match is an attribute macro that wraps your function in a `match` statement. Additionally, **it attaches rich error information to all statements using the `?` operator (aka try expressions).**
This allows you to know exactly what line and expression caused the error.

wrap-match supports both `log` and `tracing`. It defaults to `log`, but it will use `tracing` if the `tracing` feature is enabled. See [`tracing` support](#tracing-support) for more info.

> **Note**
>
> wrap-match uses the `log` or `tracing` crate to log success and error messages. It does not expose the `log` or `tracing` crate for expanded functions to use; you must depend on them yourself.
>
> Additionally, **no messages will appear unless you use a logging implementation or `tracing` subscriber.** For `log`, I recommend `env_logger`, but you can find a full list
> [here]https://docs.rs/log/#available-logging-implementations. For `tracing`, I recommend `tracing-subscriber`, but you can find a full list [here]https://docs.rs//tracing/#related-crates.

## Example

First, add this to your `Cargo.toml`:

```toml
[dependencies]
# For log users:
wrap-match = "1"
log = "*"
# You'll also want a logging implementation, for example `env_logger`
# More info here: https://docs.rs/log/#available-logging-implementations

# For tracing users:
wrap-match = { version = "1", features = ["tracing"] }
tracing = "0.1"
# You'll also want a `tracing` subscriber, for example `tracing-subscriber`
# More info here: https://docs.rs//tracing/#related-crates
```

Now you can use the `wrap_match` attribute macro:

```rust
#[wrap_match::wrap_match]
fn my_function() -> Result<(), CustomError> {
    Err(CustomError::Error)?; // notice the ?; when the macro is expanded, it will be modified to include line number and expression
    // If you need to return an error, just do `Err(CustomError::Error.into())`
    Ok(())
}
```

This would expand to something like this (comments are not included normally, and some of the output is simplified; use `cargo-expand` if you are curious what the macro really expands to):

```rust
fn my_function() -> Result<(), CustomError> {
    // This is where the original function is
    fn _wrap_match_inner_my_function() -> Result<(), WrapMatchError<CustomError>> {
        Err(CustomError::Error)
            .map_err(|e| WrapMatchError {
                // Here, line number and expression are added to the error
                line_and_expr: Some((3, "Err(CustomError::Error)")),
                inner: e.into(), // This is so you can have `Box<dyn Error>` as your error type
            })?;
        // If you need to return an error, just do `Err(CustomError::Error.into())`
        Ok(())
    }

    match _wrap_match_inner_my_function() {
        Ok(r) => {
            ::log::info!("Successfully ran my_function"); // when the tracing feature is enabled, it will use tracing macros instead
            Ok(r)
        }
        Err(e) => {
            if let Some((_line, _expr)) = e.line_and_expr {
                ::log::error!("An error occurred when running my_function (when running `{_expr}` on line {_line}): {:?}", e.inner); // when the tracing feature is enabled, it will use tracing macros instead
            } else {
                ::log::error!("An error occurred when running my_function: {:?}", e.inner); // when the tracing feature is enabled, it will use tracing macros instead
            }
            Err(e.inner)
        }
    }
}
```

If we run this code, it would log this:

```log
[ERROR] An error occurred when running my_function (when running `Err(CustomError::Error)` on line 3): Error
```

As you can see, wrap-match makes error logging extremely easy while still logging information like what caused the error.

## `tracing` support

Added in wrap-match 1.0.5, wrap-match supports `tracing` if the `tracing` feature is enabled. wrap-match **does not** do anything with spans. Additionally, you will not be able to manually create
spans in functions you use wrap-match on. This is because the span will be dropped before wrap-match logs anything.

To put both the function and wrap-match logs in a span, you have to use the `tracing::instrument` attribute macro. The ordering of the attribute macros is important; **it must go after wrap-match**.

Example:

```rust
#[wrap_match::wrap_match(success_message = "still in span!")]
#[tracing::instrument] // IMPORTANT: after wrap-match!
fn my_function() -> Result<(), ()> {
    tracing::info!("hello from tracing!");
    Ok(())
}
```

## Customization

wrap-match allows the user to customize success and error messages, as well as choosing whether or not to log anything on success.

### `success_message`

The message that's logged on success.

Available format parameters:

-   `function`: The original function name. **Note**: You can only use `{function}`; other formats such as `{function:?}` are not supported.
-   [Function arguments]#using-function-arguments-in-messages

Default value: `Successfully ran {function}`

Example:

```rust
#[wrap_match::wrap_match(success_message = "{function} ran successfully!! 🎉🎉")]
fn my_function() -> Result<(), CustomError> {
    Ok(())
}
```

This would log:

```log
[INFO] my_function ran successfully!! 🎉🎉
```

### `error_message`

The message that's logged on error, when line and expression info **is** available. Currently, this is only for try expressions (expressions with a `?` after them).

Available format parameters:

-   `function`: The original function name. **Note**: You can only use `{function}`; other formats such as `{function:?}` are not supported.
-   `line`: The line the error occurred on.
-   `expr`: The expression that caused the error.
-   `error`: The error.
-   [Function arguments]#using-function-arguments-in-messages

Default value: `` An error occurred when running {function} (caused by `{expr}` on line {line}): {error:?} ``

Example:

```rust
#[wrap_match::wrap_match(error_message = "oh no, {function} failed! `{expr}` on line {line} caused the error: {error:?}")]
fn my_function() -> Result<(), CustomError> {
    Err(CustomError::Error)?;
    Ok(())
}
```

This would log:

```log
[ERROR] oh no, my_function failed! `Err(CustomError::Error)` on line 3 caused the error: Error
```

### `error_message_without_info`

The message that's logged on error, when line and expression info **is not** available. This is usually triggered if you return an error yourself and use `.into()`.

Available format parameters:

-   `function`: The original function name. **Note**: You can only use `{function}`; other formats such as `{function:?}` are not supported.
-   `error`: The error.
-   [Function arguments]#using-function-arguments-in-messages

Default value: `An error occurred when running {function}: {error:?}`

Example:

```rust
#[wrap_match::wrap_match(error_message_without_info = "oh no, {function} failed with this error: {error:?}")]
fn my_function() -> Result<(), CustomError> {
    Err(CustomError::Error.into())
}
```

This would log:

```log
[ERROR] oh no, my_function failed with this error: Error
```

### `log_success`

If `false`, nothing will be logged on success.

Default value: `true`

Example:

```rust
#[wrap_match::wrap_match(log_success = false)]
fn my_function() -> Result<(), CustomError> {
    Ok(())
}
```

This would log nothing.

### `disregard_result`

If `true`, the resulting function will return `()` and throw away whatever the `Result` is. Useful for `main` functions.

Default value: `false`

Example:

```rust
#[wrap_match::wrap_match(disregard_result = true)]
fn main() -> Result<(), CustomError> {
    Ok(())
}
```

The `main` function would be turned into this:

```rust
fn main() {
    fn _wrap_match_inner_main() -> Result<(), WrapMatchError<CustomError>> {
        Ok(())
    }

    match _wrap_match_inner_main() {
        // the Result would be logged like normal, but it is not returned
    }
}
```

## Using function arguments in messages

As of wrap-match 1.0.5, you can use function arguments in messages, as long as they weren't moved/dropped. You should only use this for references and items that implement `Copy`.

Example:

```rust
#[wrap_match::wrap_match(success_message = "Success! input = {input}")]
fn my_function(input: i64) -> Result<i64, ()> {
    Ok(input)
}
```

## Limitations

wrap-match currently has the following limitations:

1.  ~~wrap-match cannot be used on functions in implementations that take a `self` parameter. If you need support for this, please create a GitHub issue with your use case.~~ This is now supported!
    However, it does require wrap-match to move the inner function out of the generated one, so it will add a new method to the implementation. This method is marked as deprecated, made private, and
    is not shown in documentation. Hopefully this won't cause any issues.

1.  wrap-match only supports `Result`s. If you need support for `Option`s, please create a GitHub issue with your use case.

1.  ~~`error_message` and `error_message_without_info` only support formatting `error` using the `Debug` or `Display` formatters. This is because of how we determine what formatting specifiers are
    used. If you need support for other formatting specifiers, please create a GitHub issue with your use case.~~ All format parameters (except `function`) now support all basic formats that `format!`
    supports (however, features such as precision, sign, fill, alignment and width will most likely never be supported).

1.  wrap-match cannot be used on `const` functions. This is because the `log` crate cannot be used in `const` contexts.

If wrap-match doesn't work for something not on this list, please create a GitHub issue!