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
//! Zebrad Subcommands

mod connect;
mod generate;
mod revhex;
mod seed;
mod start;
mod version;

use self::{
    connect::ConnectCmd, generate::GenerateCmd, revhex::RevhexCmd, seed::SeedCmd, start::StartCmd,
    version::VersionCmd,
};
use crate::config::ZebradConfig;
use abscissa_core::{
    config::Override, Command, Configurable, FrameworkError, Help, Options, Runnable,
};
use std::path::PathBuf;

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

/// Zebrad Subcommands
#[derive(Command, Debug, Options, Runnable)]
pub enum ZebradCmd {
    /// The `generate` subcommand
    #[options(help = "generate a skeleton configuration")]
    Generate(GenerateCmd),

    /// The `connect` subcommand
    #[options(help = "testing stub for dumping network messages")]
    Connect(ConnectCmd),

    /// The `help` subcommand
    #[options(help = "get usage information")]
    Help(Help<Self>),

    /// The `revhex` subcommand
    #[options(help = "reverses the endianness of a hex string, like a block or transaction hash")]
    Revhex(RevhexCmd),

    /// The `seed` subcommand
    #[options(help = "dns seeder")]
    Seed(SeedCmd),

    /// The `start` subcommand
    #[options(help = "start the application")]
    Start(StartCmd),

    /// The `version` subcommand
    #[options(help = "display version information")]
    Version(VersionCmd),
}

impl ZebradCmd {
    /// Returns true if this command sends output to stdout.
    ///
    /// For example, `Generate` sends a default config file to stdout.
    ///
    /// Usage note: `abscissa_core::EntryPoint` stores an `Option<ZerbradCmd>`.
    /// If the command is `None`, then abscissa writes zebrad usage information
    /// to stdout.
    pub(crate) fn uses_stdout(&self) -> bool {
        use ZebradCmd::*;
        match self {
            // List all the commands, so new commands have to make a choice here
            Generate(_) | Help(_) | Revhex(_) | Version(_) => true,
            Connect(_) | Seed(_) | Start(_) => false,
        }
    }
}

/// 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 filename = std::env::current_dir().ok().map(|mut dir_path| {
            dir_path.push(CONFIG_FILE);
            dir_path
        });

        let if_exists = |f: PathBuf| if f.exists() { Some(f) } else { None };

        filename.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),
        }
    }
}