//! 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, } /// 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 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, }, }