diff --git a/Cargo.toml b/Cargo.toml index 199a6e3e92..5713e23180 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,3 +31,4 @@ tokio-stream = { version = "0.1.4" } tokio-postgres = { git = "https://github.com/kelvich/rust-postgres", branch = "replication_rebase" } postgres-protocol = { git = "https://github.com/kelvich/rust-postgres", branch = "replication_rebase" } postgres = { git = "https://github.com/kelvich/rust-postgres", branch = "replication_rebase" } +anyhow = "1.0" diff --git a/src/bin/cli/main.rs b/src/bin/cli/main.rs new file mode 100644 index 0000000000..fffdfb9a97 --- /dev/null +++ b/src/bin/cli/main.rs @@ -0,0 +1,46 @@ +use clap::{App, AppSettings}; +use anyhow::{Result}; + +mod subcommand; +pub mod pg; +pub mod storage; +pub mod snapshot; + +fn main() -> Result<()> { + + let cli_commands = subcommand::ClapCommands { + commands: vec![ + Box::new(pg::PgCmd { + clap_cmd: clap::SubCommand::with_name("pg"), + }), + Box::new(storage::StorageCmd { + clap_cmd: clap::SubCommand::with_name("storage"), + }), + Box::new(snapshot::SnapshotCmd { + clap_cmd: clap::SubCommand::with_name("snapshot"), + }), + ], + }; + + + let matches = App::new("zenith") + .about("Zenith CLI") + .version("1.0") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommands(cli_commands.generate()) + .get_matches(); + + + if let Some(subcommand) = matches.subcommand_name() { + println!("'git {}' was used", subcommand); + } + + match matches.subcommand() { + ("pg", Some(sub_args)) => cli_commands.commands[0].run(sub_args.clone())?, + ("storage", Some(sub_args)) => cli_commands.commands[1].run(sub_args.clone())?, + ("snapshot", Some(sub_args)) => cli_commands.commands[2].run(sub_args.clone())?, + ("", None) => println!("No subcommand"), + _ => unreachable!(), + } + Ok(()) +} diff --git a/src/bin/cli/pg.rs b/src/bin/cli/pg.rs new file mode 100644 index 0000000000..534bcef426 --- /dev/null +++ b/src/bin/cli/pg.rs @@ -0,0 +1,41 @@ +use clap::{App, AppSettings, Arg}; +use anyhow::{Result}; + +use crate::subcommand; + +pub struct PgCmd<'a> { + pub clap_cmd: clap::App<'a, 'a>, +} + +impl subcommand::SubCommand for PgCmd<'_> { + fn gen_clap_command(&self) -> clap::App { + let c = self.clap_cmd.clone(); + c.about("Operations with zenith compute nodes") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + App::new("list") + ) + .subcommand( + App::new("create") + .arg(Arg::with_name("pgdata").required(true)), + ) + .subcommand( + App::new("destroy") + ) + .subcommand( + App::new("start") + ) + .subcommand( + App::new("stop") + ) + .subcommand( + App::new("show") + ) + } + + fn run(&self, args: clap::ArgMatches) -> Result<()> { + + println!("Run PgCmd with args {:?}", args); + Ok(()) + } +} \ No newline at end of file diff --git a/src/bin/cli/snapshot.rs b/src/bin/cli/snapshot.rs new file mode 100644 index 0000000000..c149d7318e --- /dev/null +++ b/src/bin/cli/snapshot.rs @@ -0,0 +1,41 @@ +use clap::{App, AppSettings, Arg}; +use anyhow::{Result}; + +use crate::subcommand; + +pub struct SnapshotCmd<'a> { + pub clap_cmd: clap::App<'a, 'a>, +} + +impl subcommand::SubCommand for SnapshotCmd<'_> { + fn gen_clap_command(&self) -> clap::App { + let c = self.clap_cmd.clone(); + c.about("Operations with zenith snapshots") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + App::new("list") + ) + .subcommand( + App::new("create") + .arg(Arg::with_name("pgdata").required(true)), + ) + .subcommand( + App::new("destroy") + ) + .subcommand( + App::new("start") + ) + .subcommand( + App::new("stop") + ) + .subcommand( + App::new("show") + ) + } + + fn run(&self, args: clap::ArgMatches) -> Result<()> { + + println!("Run SnapshotCmd with args {:?}", args); + Ok(()) + } +} \ No newline at end of file diff --git a/src/bin/cli/storage.rs b/src/bin/cli/storage.rs new file mode 100644 index 0000000000..9f37c38ac7 --- /dev/null +++ b/src/bin/cli/storage.rs @@ -0,0 +1,34 @@ +use clap::{App, AppSettings}; +use anyhow::{Result}; + +use crate::subcommand; + +pub struct StorageCmd<'a> { + pub clap_cmd: clap::App<'a, 'a>, +} + +impl subcommand::SubCommand for StorageCmd<'_> { + fn gen_clap_command(&self) -> clap::App { + let c = self.clap_cmd.clone(); + c.about("Operations with zenith storage nodes") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + App::new("list") + ) + .subcommand( + App::new("attach") + ) + .subcommand( + App::new("detach") + ) + .subcommand( + App::new("show") + ) + } + + fn run(&self, args: clap::ArgMatches) -> Result<()> { + + println!("Run StorageCmd with args {:?}", args); + Ok(()) + } +} \ No newline at end of file diff --git a/src/bin/cli/subcommand.rs b/src/bin/cli/subcommand.rs new file mode 100644 index 0000000000..6a9e7363b9 --- /dev/null +++ b/src/bin/cli/subcommand.rs @@ -0,0 +1,29 @@ +use anyhow::Result; + +/// All subcommands need to implement this interface. +pub trait SubCommand { + /// Generates the cli-config that Clap requires for the subcommand. + fn gen_clap_command(&self) -> clap::App; + + /// Runs the body of the subcommand. + fn run(&self, args: clap::ArgMatches) -> Result<()>; +} + +/// A struct which holds a vector of heap-allocated `Box`es of trait objects all of which must +/// implement the `SubCommand` trait, but other than that, can be of any type. +pub struct ClapCommands { + pub commands: Vec>, +} + +impl ClapCommands { + /// Generates a vector of `clap::Apps` that can be passed into clap's `.subcommands()` method in + /// order to generate the full CLI. + pub fn generate(&self) -> Vec { + let mut v: Vec = Vec::new(); + + for command in self.commands.iter() { + v.push(command.gen_clap_command()); + } + v + } +} diff --git a/src/bin/zenith-cli.rs b/src/bin/zenith-cli.rs deleted file mode 100644 index f5bcb2e5cc..0000000000 --- a/src/bin/zenith-cli.rs +++ /dev/null @@ -1,101 +0,0 @@ - -use clap::{App, AppSettings, Arg}; - -fn main() { - - let matches = App::new("zenith") - .about("Zenith CLI") - .version("1.0") - .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand( - App::new("pg") - .about("compute node postgres") - .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand( - App::new("list") - ) - .subcommand( - App::new("create") - .arg(Arg::with_name("pgdata").required(true)), - ) - .subcommand( - App::new("destroy") - ) - .subcommand( - App::new("start") - ) - .subcommand( - App::new("stop") - ) - .subcommand( - App::new("show") - ), - ) - .subcommand( - App::new("storage") - .about("storage node postgres") - .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand( - App::new("list") - ) - .subcommand( - App::new("attach") - ) - .subcommand( - App::new("detach") - ) - .subcommand( - App::new("show") - ), - ) - .get_matches(); - - - if let Some(subcommand) = matches.subcommand_name() { - println!("'git {}' was used", subcommand); - } - - match matches.subcommand() { - ("pg", Some(pg_matches)) => { - println!("pg subcommand"); - match pg_matches.subcommand() - { - ("list", _) => { - println!("list subcommand"); - } - ("create", _) => { - } - ("destroy", _) => { - } - ("start", _) => { - } - ("stop", _) => { - } - ("show", _) => { - } - ("", None) => println!("No subcommand"), - _ => unreachable!(), - } - } - ("storage", Some(storage_matches)) => { - println!("storage subcommand"); - match storage_matches.subcommand() - { - ("list", _) => { - println!("list subcommand"); - } - ("attach", _) => { - } - ("detach", _) => { - } - ("show", _) => { - } - ("", None) => println!("No subcommand"), - _ => unreachable!(), - } - } - - ("", None) => println!("No subcommand"), - _ => unreachable!(), - } -}