diff options
Diffstat (limited to 'ipfs-cli/src/command/mod.rs')
-rw-r--r-- | ipfs-cli/src/command/mod.rs | 118 |
1 files changed, 117 insertions, 1 deletions
diff --git a/ipfs-cli/src/command/mod.rs b/ipfs-cli/src/command/mod.rs index c19236b..3f6d4b3 100644 --- a/ipfs-cli/src/command/mod.rs +++ b/ipfs-cli/src/command/mod.rs @@ -6,10 +6,14 @@ // copied, modified, or distributed except according to those terms. // +use clap::{App, ArgMatches}; +use futures::Future; +use ipfs_api::IpfsClient; use std::error::Error; use std::fs; -pub const EXPECTED_API: &str = "expected response from API"; +pub type CommandExecutable = Box<Future<Item = (), Error = ()> + 'static + Send>; + pub const EXPECTED_FILE: &str = "expected to read input file"; /// Verifies that a path points to a file that exists, and not a directory. @@ -22,6 +26,118 @@ pub fn verify_file(path: String) -> Result<(), String> { } } +pub trait CliCommand { + /// Name of the command. + /// + const NAME: &'static str; + + /// Returns the signature of the application. + /// + fn signature<'a, 'b>() -> App<'a, 'b>; + + /// Creates a future representing the request to make. + /// + fn handle(client: &IpfsClient, args: &ArgMatches) -> CommandExecutable; +} + +macro_rules! handle_case { + // Base macro case. Converts an expression into a boxed future. + // + ($run: expr) => { + { + let future = $run; + + return Box::new(future.map_err(|e| eprintln!("{}", e))) + } + }; + // Base case for nested subcommand (e.g. /bootstrap/add). + // + ( + $subcommand: ident; + ($key: pat) => { $(($inner_key: pat, $args: ident) => $run: expr),* } + ) => { + if let ($key, Some(args)) = $subcommand { + let inner_subcommand = args.subcommand(); + + $( + handle_case!(inner_subcommand; ($inner_key, $args) => $run); + )* + } + }; + // Base case for subcommand. + // + ( + $subcommand: ident; + ($key: pat, $args: pat) => $run: expr + ) => { + if let ($key, Some($args)) = $subcommand { + handle_case!($run) + } + }; + // Recursive case for nested subcommand. + // + ( + $subcommand: ident; + ($key: pat) => { $(($inner_key: pat, $args: ident) => $run: expr),* }, + $($rest_args: tt => $rest_run: tt),* + ) => { + handle_case!($subcommand; ($key) => { $(($inner_key, $args) => $run),* }); + + $( + handle_case!($subcommand; $rest_args => $rest_run); + )* + }; + // Recursive case fo subcommand. + // + ( + $subcommand: ident; + ($key: pat, $args: pat) => $run: expr, + $($rest_args: tt => $rest_run: tt),* + ) => { + handle_case!($subcommand; ($key, $args) => $run); + + $( + handle_case!($subcommand; $rest_args => $rest_run); + )* + } +} + +macro_rules! handle { + // Command with no subcommands. + // + ( + ($args: ident, $client: ident) => $run: expr + ) => { + fn handle( + client: &::ipfs_api::IpfsClient, + args: &::clap::ArgMatches, + ) -> ::command::CommandExecutable { + let $args = args; + let $client = client; + + handle_case!($run) + } + }; + // Command with one or more subcommands. + // + ( + $client: ident; + $($args: tt => $run: tt),* + ) => { + fn handle( + client: &::ipfs_api::IpfsClient, + args: &::clap::ArgMatches, + ) -> ::command::CommandExecutable { + let $client = client; + let subcommand = args.subcommand(); + + handle_case!(subcommand; $($args => $run),*); + + unreachable!() + } + } +} + pub mod add; pub mod bitswap; pub mod block; |