Files
payphone/src/cli.rs
T

170 lines
5.6 KiB
Rust

//! Payphone's main entry point.
//!
//! This module is responsible for parsing command line arguments, nothing else.
//!
//! # Command Line Arguments
//!
//! The command line arguments are parsed using the [`clap`] crate, and can be found in the
//! [`Args`] struct. The arguments are:
//!
//! - `[TOKEN]`: The Discord bot token to run the bot with. This is required.
//! - `--logging` or `-l`: The minimum level logs have to be to be outputted into the console, or
//! off for no logs. Defaults to `info`.
//! - `--database-addr` or `-d`: The address of the database to connect to, a Scylla node.
//!
//! The CLI also supports setting argument values via environment variables. The environment
//! variables are prefixed with `PAYPHONE_`, so for example, the `--token` argument can be set
//! using the `PAYPHONE_TOKEN` environment variable. The environment variables are used to
//! override the command line arguments. `.env` files are also supported by default, so you can
//! create a `.env` file in the root of the project with the following content:
//!
//! ```env
//! PAYPHONE_TOKEN="your_discord_bot_token"
//! PAYPHONE_LOGGING="info"
//! PAYPHONE_DATABASE_ADDR="your_database_addr"
//! ```
//!
//! # Parsing Arguments
//!
//! The arguments are parsed using the `Args::parse()` method, which returns an instance of the
//! [`Args`] struct. This struct contains the parsed values of the command line arguments, and
//! can be used to initialize the bot and start the event loop.
//!
//! ```
//! // Read the environment variables from the `.env` file, if it exists, and load them as proper
//! // env variables.
//! dotenvy::dotenv().ok();
//!
//! let args = Args::parse();
//! ```
//!
//! # Running the Bot
//!
//! To start the bot, run:
//!
//! ```shell
//! $ cargo run -- start [TOKEN]
//! ```
//!
//! # Running the Documentation Server
//!
//! Payphone has a built-in documentation server that serves the project's documentation and
//! refreshes it on changes. To start the documentation server, run the following command:
//!
//! ```shell
//! $ cargo run -- docs --open
//! ```
//!
//! The `--open` flag is optional and will open the documentation in the default browser after
//! starting the server. You can also use the `--bind` argument to specify the address to bind the
//! server to, which defaults to `0.0.0.0:3000`.
use std::net::SocketAddr;
/// Payphone's argument parser.
#[derive(clap::Parser)]
#[command(name = "Payphone CLI")]
#[command(version, about = "Payphone's command line interface.")]
pub struct Args {
/// The minimum level logs have to be to be outputted into the console, or off for no logs.
#[arg(
long,
short,
value_enum,
default_value_t = LevelFilter::Info,
env = "PAYPHONE_LOGGING",
value_name = "LEVEL",
long_help = "The minimum level logs have to be to be outputted into the console, or off for no logs."
)]
pub logging: LevelFilter,
#[command(subcommand)]
pub command: Option<Commands>,
}
/// An enum with the different logging levels that can be used in the CLI.
///
/// This enum is used to parse the `--logging` or `-l` command line argument and then map the value
/// to [`log::LevelFilter`].
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
pub enum LevelFilter {
/// No logs at all.
Off,
/// The most verbose level, used for debugging purposes.
Trace,
/// Events that are useful to be displayed during development, but not in production.
Debug,
/// Used for general information about the bot's state and milestones.
Info,
/// Used for warnings, when something unexpected happens but the bot can still continue
/// running.
Warn,
/// Used for errors, when something goes wrong and the bot cannot continue running.
Error,
}
#[allow(clippy::from_over_into)]
impl Into<log::LevelFilter> for LevelFilter {
fn into(self) -> log::LevelFilter {
match self {
Self::Off => log::LevelFilter::Off,
Self::Trace => log::LevelFilter::Trace,
Self::Debug => log::LevelFilter::Debug,
Self::Info => log::LevelFilter::Info,
Self::Warn => log::LevelFilter::Warn,
Self::Error => log::LevelFilter::Error,
}
}
}
#[derive(clap::Subcommand, Clone)]
pub enum Commands {
/// Starts the bot with the provided token.
Start {
/// The Discord bot token to run the bot with.
#[arg(env = "PAYPHONE_TOKEN")]
token: String,
/// The socket address of the database to connect to, a Scylla node.
#[arg(
long,
short,
env = "PAYPHONE_DATABASE_ADDR",
value_name = "ADDR",
default_value_t = String::from("127.0.0.1:9042"))
]
database_addr: String,
},
/// Starts a documentation server for the project which serves `cargo doc` and refreshes on
/// changes.
Docs {
/// The address to run the documentation server on.
#[arg(
long,
short,
env = "PAYPHONE_DOCS_ADDR",
value_name = "ADDR",
default_value_t = SocketAddr::from(([0, 0, 0, 0], 3000))
)]
bind: SocketAddr,
/// When this flag is set, the documentation server will open the documentation in the
/// default browser after starting the server.
#[arg(
long,
short,
env = "PAYPHONE_DOCS_OPEN",
value_name = "OPEN",
default_value_t = false,
help = "Open the documentation in the default browser after starting the server."
)]
open: bool,
},
}