eliminate main function boilerplate with this opinionated application framework/wrapper
entrypoint
has the following design goals:
- eliminate application startup/configuration boilerplate
- help enforce application best practices
entrypoint
wraps a user defined function with automatic configuration/setup/processing of:
- easy application error handling (via
anyhow
) - command-line argument parsing (via
clap
) .dotenv
file processing and environment variable population/overrides (viadotenvy
)- logging (via
tracing
)
The user defined function is intended to be/replace main()
.
Meaning, this main/entrypoint function can be written as if all the configuration/processing/boilerplate is ready-to-use. More explicitly:
anyhow
is available and ready to useclap::Parser
struct has been parsed and populated.dotenv
files have been parsed; environment variables are ready to gotracing
has been configured and the global subscriber has been registered
entrypoint
was as much about deploying my first crate as anything else.
Turns out, it's really not that useful. I kind of think it's better just to explicitly set this stuff up in your application. The juice isn't worth the squeeze.
It's unlikely further development will occur.
-
Include the
entrypoint
prelude:use entrypoint::prelude::*;
-
Define a
clap
struct and derive default entrypoint trait impls:#[derive(clap::Parser, DotEnvDefault, LoggerDefault, Debug)] #[log_format(full)] #[log_level(entrypoint::tracing::Level::INFO)] #[command(version, about, long_about = None)] struct CLIArgs { #[arg(short, long, env)] cli_arg: bool, }
-
Define an entrypoint/main function:
#[entrypoint::entrypoint] fn entrypoint(args: CLIArgs) -> entrypoint::anyhow::Result<()> { // args are parsed and ready to use info!("cli_arg set to: {:?}", args.cli_arg); // env::vars() already loaded-from/merged-with .dotenv file(s) let _my_var = env::vars("SOMETHING_FROM_DOTENV_FILE"); // logging is ready to use info!("entrypoint::entrypoint"); Ok(()) }
Using the default behavior is totally reasonable, but overwriting some default impl(s) can provide customization.
- The
entrypoint
function must:- Have a
clap::Parser
input parameter - return
entrypoint::anyhow::Result<()>
- Have a
#[entrypoint::entrypoint]
ordering may matter when used with other attribute macros (e.g.[tokio::main]
).
For more information, refer to:
entrypoint
is divided into the following crates:
entrypoint
: core traits and functionalityentrypoint_macros
: convienence macros to further reduce boilerplate
Before doing anything else: open an issue.