1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! Zebrad Subcommands

use std::path::PathBuf;

use abscissa_core::{config::Override, Command, Configurable, FrameworkError, Runnable};

use crate::config::ZebradConfig;

pub use self::{entry_point::EntryPoint, start::StartCmd};

use self::{copy_state::CopyStateCmd, generate::GenerateCmd, tip_height::TipHeightCmd};

pub mod start;

mod copy_state;
mod entry_point;
mod generate;
mod tip_height;

#[cfg(test)]
mod tests;

use ZebradCmd::*;

/// Zebrad Configuration Filename
pub const CONFIG_FILE: &str = "zebrad.toml";

/// Zebrad Subcommands
#[derive(Command, Debug, clap::Subcommand)]
pub enum ZebradCmd {
    /// The `copy-state` subcommand, used to debug cached chain state (expert users only)
    // TODO: hide this command from users in release builds (#3279)
    CopyState(CopyStateCmd),

    /// Generate a default `zebrad.toml` configuration
    Generate(GenerateCmd),

    /// Start the application (default command)
    Start(StartCmd),

    /// Print the tip block height of Zebra's chain state on disk
    TipHeight(TipHeightCmd),
}

impl ZebradCmd {
    /// Returns true if this command is a server command.
    ///
    /// Servers load extra components, and use the configured tracing filter.
    ///
    /// For example, `Start` acts as a Zcash node.
    pub(crate) fn is_server(&self) -> bool {
        // List all the commands, so new commands have to make a choice here
        match self {
            // Commands that run as a configured server
            CopyState(_) | Start(_) => true,

            // Utility commands that don't use server components
            Generate(_) | TipHeight(_) => false,
        }
    }

    /// Returns true if this command shows the Zebra intro logo and text.
    ///
    /// For example, `Start` acts as a Zcash node.
    pub(crate) fn uses_intro(&self) -> bool {
        // List all the commands, so new commands have to make a choice here
        match self {
            // Commands that need an intro
            Start(_) => true,

            // Utility commands
            CopyState(_) | Generate(_) | TipHeight(_) => false,
        }
    }

    /// Returns true if this command should ignore errors when
    /// attempting to load a config file.
    pub(crate) fn should_ignore_load_config_error(&self) -> bool {
        matches!(self, ZebradCmd::Generate(_))
    }

    /// Returns the default log level for this command, based on the `verbose` command line flag.
    ///
    /// Some commands need to be quiet by default.
    pub(crate) fn default_tracing_filter(&self, verbose: bool) -> &'static str {
        let only_show_warnings = match self {
            // Commands that generate quiet output by default.
            // This output:
            // - is used by automated tools, or
            // - needs to be read easily.
            Generate(_) | TipHeight(_) => true,

            // Commands that generate informative logging output by default.
            CopyState(_) | Start(_) => false,
        };

        if only_show_warnings && !verbose {
            "warn"
        } else if only_show_warnings || !verbose {
            "info"
        } else {
            "debug"
        }
    }
}

impl Runnable for ZebradCmd {
    fn run(&self) {
        match self {
            CopyState(cmd) => cmd.run(),
            Generate(cmd) => cmd.run(),
            Start(cmd) => cmd.run(),
            TipHeight(cmd) => cmd.run(),
        }
    }
}

/// This trait allows you to define how application configuration is loaded.
impl Configurable<ZebradConfig> for ZebradCmd {
    /// Location of the configuration file
    fn config_path(&self) -> Option<PathBuf> {
        let if_exists = |f: PathBuf| if f.exists() { Some(f) } else { None };

        dirs::preference_dir()
            .map(|path| path.join(CONFIG_FILE))
            .and_then(if_exists)

        // Note: Changes in how configuration is loaded may need usage
        // edits in generate.rs
    }

    /// Apply changes to the config after it's been loaded, e.g. overriding
    /// values in a config file using command-line options.
    ///
    /// This can be safely deleted if you don't want to override config
    /// settings from command-line options.
    fn process_config(&self, config: ZebradConfig) -> Result<ZebradConfig, FrameworkError> {
        match self {
            ZebradCmd::Start(cmd) => cmd.override_config(config),
            _ => Ok(config),
        }
    }
}