grapevine/src/cli.rs

119 lines
3.1 KiB
Rust

//! Integration with `clap`
//!
//! CLI argument structs are defined in this module. Execution logic for each
//! command goes in a submodule.
use std::path::PathBuf;
use clap::{Parser, Subcommand};
use crate::{
config::{default_tracing_filter, EnvFilterClone, LogFormat},
error, observability,
};
mod check_config;
mod serve;
/// Command line arguments
#[derive(Parser)]
#[clap(
about,
version = crate::version(),
)]
pub(crate) struct Args {
#[clap(subcommand)]
pub(crate) command: Command,
}
#[derive(Subcommand)]
pub(crate) enum Command {
/// Run the server.
Serve(ServeArgs),
/// Check the configuration file for syntax and semantic errors.
CheckConfig(CheckConfigArgs),
}
#[derive(clap::Args)]
pub(crate) struct CheckConfigArgs {
#[clap(flatten)]
config: ConfigArg,
#[clap(flatten)]
observability: ObservabilityArgs,
}
/// Wrapper for the `--config` arg.
///
/// This exists to centralize the `mut_arg` code that sets the help value based
/// on runtime information.
#[derive(clap::Args)]
#[clap(mut_arg("config", |x| {
let help = "Set the path to the configuration file";
x.help(help).long_help(format!(
"{}\n\nIf this option is specified, the provided value is used \
as-is.\n\nIf this option is not specified, then the XDG Base \
Directory Specification is followed, searching for the path `{}` \
in the configuration directories.
",
help,
crate::config::DEFAULT_PATH.display(),
))
}))]
pub(crate) struct ConfigArg {
/// Path to the configuration file
#[clap(long, short)]
pub(crate) config: Option<PathBuf>,
}
/// Observability arguments for CLI subcommands
#[derive(clap::Args)]
struct ObservabilityArgs {
/// Log format
#[clap(long, default_value_t = LogFormat::Full)]
log_format: LogFormat,
/// Log filter
///
/// For information about the syntax, see here:
/// <https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives>
#[clap(long, default_value_t = default_tracing_filter())]
log_filter: EnvFilterClone,
}
#[derive(clap::Args)]
pub(crate) struct ServeArgs {
#[clap(flatten)]
pub(crate) config: ConfigArg,
}
impl Args {
pub(crate) async fn run(self) -> Result<(), error::Main> {
if let Some((format, filter)) = self.command.cli_observability_args() {
observability::init_for_cli(format, filter.into())?;
}
match self.command {
Command::Serve(args) => serve::run(args).await?,
Command::CheckConfig(args) => {
check_config::run(args.config).await?;
}
}
Ok(())
}
}
impl Command {
fn cli_observability_args(&self) -> Option<(LogFormat, EnvFilterClone)> {
// All subcommands other than `serve` should return `Some`. Keep these
// match arms sorted by the enum variant name.
match self {
Command::CheckConfig(args) => Some((
args.observability.log_format,
args.observability.log_filter.clone(),
)),
Command::Serve(_) => None,
}
}
}