1//! Zebrad EntryPoint
23use abscissa_core::{Command, Configurable, FrameworkError, Runnable};
4use clap::Parser;
5use std::{ffi::OsString, path::PathBuf};
67use crate::config::ZebradConfig;
89use super::ZebradCmd;
1011/// Toplevel entrypoint command.
12///
13/// Handles obtaining toplevel help as well as verbosity settings.
14#[derive(Debug, clap::Parser)]
15#[clap(
16 version = clap::crate_version!(),
17 author="Zcash Foundation <zebra@zfnd.org>",
18 help_template = "\
19{name} {version}\n
20{author}\n
21{usage-heading} {usage}\n
22{all-args}\
23"
24)]
25pub struct EntryPoint {
26/// Subcommand to execute.
27 ///
28 /// The `command` option will delegate option parsing to the command type,
29 /// starting at the first free argument. Defaults to start.
30#[clap(subcommand)]
31pub cmd: Option<ZebradCmd>,
3233/// Path to the configuration file
34#[clap(long, short, help = "path to configuration file")]
35pub config: Option<PathBuf>,
3637/// Increase verbosity setting
38#[clap(long, short, help = "be verbose")]
39pub verbose: bool,
4041/// Filter strings which override the config file and defaults
42// This can be applied to the default start command if no subcommand is provided.
43#[clap(long, help = "tracing filters which override the zebrad.toml config")]
44filters: Vec<String>,
45}
4647impl EntryPoint {
48/// Borrow the command in the option
49 ///
50 /// # Panics
51 ///
52 /// If `cmd` is None
53pub fn cmd(&self) -> &ZebradCmd {
54self.cmd
55 .as_ref()
56 .expect("should default to start if not provided")
57 }
5859/// Returns a string that parses to the default subcommand
60pub fn default_cmd_as_str() -> &'static str {
61"start"
62}
6364/// Checks if the provided arguments include a subcommand
65fn should_add_default_subcommand(&self) -> bool {
66self.cmd.is_none()
67 }
6869/// Process command arguments and insert the default subcommand
70 /// if no subcommand is provided.
71pub fn process_cli_args(mut args: Vec<OsString>) -> clap::error::Result<Vec<OsString>> {
72let entry_point = EntryPoint::try_parse_from(&args)?;
7374// Add the default subcommand to args after the top-level args if cmd is None
75if entry_point.should_add_default_subcommand() {
76 args.push(EntryPoint::default_cmd_as_str().into());
77// This duplicates the top-level filters args, but the tracing component only checks `StartCmd.filters`.
78for filter in entry_point.filters {
79 args.push(filter.into())
80 }
81 }
8283Ok(args)
84 }
85}
8687impl Runnable for EntryPoint {
88fn run(&self) {
89self.cmd().run()
90 }
91}
9293impl Command for EntryPoint {
94/// Name of this program as a string
95fn name() -> &'static str {
96 ZebradCmd::name()
97 }
9899/// Description of this program
100fn description() -> &'static str {
101 ZebradCmd::description()
102 }
103104/// Authors of this program
105fn authors() -> &'static str {
106 ZebradCmd::authors()
107 }
108}
109110impl Configurable<ZebradConfig> for EntryPoint {
111/// Path to the command's configuration file
112fn config_path(&self) -> Option<PathBuf> {
113match &self.config {
114// Use explicit `-c`/`--config` argument if passed
115Some(cfg) => Some(cfg.clone()),
116117// Otherwise defer to the toplevel command's config path logic
118None => self.cmd().config_path(),
119 }
120 }
121122/// Process the configuration after it has been loaded, potentially
123 /// modifying it or returning an error if options are incompatible
124fn process_config(&self, config: ZebradConfig) -> Result<ZebradConfig, FrameworkError> {
125self.cmd().process_config(config)
126 }
127}