exit_safely 0.3.2

exit safely with custom exit codes while still calling `Drop` as needed. Aims for minimal magic and maximum flexibilty.
Documentation
# exit_safely - with Drop & meaningful exit codes

`exit_safely` provides a simple and highly transparent option to `derive(Termination)` from
your own enum with a very simple API which still provides you full control over exit codes
and what to (safely) output to stderr.

Minimal magic, maximum flexibility, zero boilerplate.

## Why?

[`std::process::exit`](https://doc.rust-lang.org/std/process/fn.exit.html) warns: "Note that
because this function never returns, and that it terminates the process, no destructors on the
current stack or any other thread’s stack will be run. If a clean shutdown is needed it is
recommended to ... simply return a type implementing Termination ... from the main function
and avoid this function altogether"

## Example on nightly

For the best use in `main()` you will probably also want to derive `Debug` and `Try`
(via [try_v2](https://docs.rs/try_v2/latest/try_v2/)):

```rust
#![cfg_attr(unstable_never_type, feature(never_type))]
#![cfg_attr(unstable_try_trait_v2, feature(try_trait_v2))]
#![cfg_attr(unstable_try_trait_v2_residual, feature(try_trait_v2_residual))]
use std::process::Termination as _T;
use exit_safely::Termination;
use try_v2::{Try, Try_ConvertResult};

/// First define your exit codes:
#[derive(Debug, Termination, Try, Try_ConvertResult)]
#[must_use]
#[repr(u8)]
enum Exit<T: _T> {
    Ok(T) = 0,
    Error(String) = 1,
    InvocationError(String) = 2,
}

/// Then any conversion:
/// clap errors return exit_code 2 & output the details
/// to stderr, letting clap handle formatting
impl<T: _T> From<clap::Error> for Exit<T> {
    fn from(err: clap::Error) -> Self {
        Self::InvocationError(err.to_string())
    }
}

fn main() -> Exit<()> {
    // Use `?` to return the right exit code for the error type
    let cli = Cli::try_parse()?;

    // ok_or()? converts a missing value to an exit
    let value = inputs
        .next()
        .ok_or(Exit::Error("Not enough input, need more cheese".to_string()))?;

    // if your central processing returns a value which might be invalid
    Exit::from(process(inputs)?)

    // or simply return `Exit::...`
    // Exit::Ok(())
}
```

## Example on stable

If you prefer not to use a nightly toolchain then `exit_safely` still works fine, although
you cannot leverage the power of `?` just yet. The same example looks more like the pattern
if you used std::process::exit (or go)

```rust
use std::process::Termination as _T;
use exit_safely::Termination;

/// First define your exit codes:
#[derive(Debug, Termination)]
#[must_use]
#[repr(u8)]
enum Exit<T: _T> {
    Ok(T) = 0,
    Error(String) = 1,
    InvocationError(String) = 2,
}

/// Then any conversion:
/// clap errors return exit_code 2 & output the details
/// to stderr, letting clap handle formatting
impl<T: _T> From<clap::Error> for Exit<T> {
    fn from(err: clap::Error) -> Self {
        Self::InvocationError(err.to_string())
    }
}

fn main() -> Exit<()> {
    // Match on `Results` and use `into` to return the right exit code for the error type
    let cli = match Cli::try_parse() {
        Ok(cli) => cli,
        Err(e) => return e.into(), // or `Exit::from(e)` if you prefer
    };

    // `let ... else` converts a missing value to an exit
    let Some(value) = inputs.next() else {
        return Exit::Error("Not enough input, need more cheese".to_string())
    };

    process(inputs);
    Exit::Ok(())
}
```

> 🔬 **Stability**
>
> This crate makes use of the following experimental features if they are available but
> **does not require** any experimental features to work.
>
> ### For improved compiler errors
>
> - [`#![feature(proc_macro_diagnostic)]`]https://github.com/rust-lang/rust/issues/54140
>
> To provide nicer compiler errors with "help", "notes" and a warning if you forget the repr(u8)
>
> On current stable help & notes are output as extra errors, but warnings are not possible
>
> ### Recommended: try_trait_v2, try_trait_v2_residual & never_type
>
> - [`#![feature(never_type)]`]https://github.com/rust-lang/rust/issues/35121
> - [`#![feature(try_trait_v2)]`]https://github.com/rust-lang/rust/issues/84277
> - [`#![feature(try_trait_v2_residual)]`]https://github.com/rust-lang/rust/issues/91285
>
> I find the ergonomics work best for types which also implement the experimental `Try` and
> created the crate [`try_v2`]https://crates.io/crates/try_v2 to make it easy for you to
> take advantage.
>
> I consider all of the above features to be reliable and already well advanced in the
> stabilisation process. Nevertheless, I run automated tests **every month** and on every PR
> against stable, beta & nightly to ensure no fundamental changes affect this crate. I get a
> direct notification on my phone for any failures.
>
> Feel free to check out the build script, workflows and source to see how I do this.