From b786be1e6087a45f1db236f30d91803710c41274 Mon Sep 17 00:00:00 2001 From: Ferris Tseng Date: Wed, 27 Jun 2018 15:54:50 -0400 Subject: upgrade for hyper 0.12; use macros to build commands --- ipfs-cli/Cargo.toml | 2 +- ipfs-cli/src/command/add.rs | 50 ++++--- ipfs-cli/src/command/bitswap.rs | 173 ++++++++++++----------- ipfs-cli/src/command/block.rs | 128 +++++++++-------- ipfs-cli/src/command/bootstrap.rs | 88 ++++++------ ipfs-cli/src/command/cat.rs | 41 +++--- ipfs-cli/src/command/commands.rs | 38 ++--- ipfs-cli/src/command/config.rs | 67 ++++----- ipfs-cli/src/command/dag.rs | 64 +++++---- ipfs-cli/src/command/dht.rs | 121 ++++++++-------- ipfs-cli/src/command/diag.rs | 117 ++++++++-------- ipfs-cli/src/command/dns.rs | 47 ++++--- ipfs-cli/src/command/file.rs | 92 +++++++------ ipfs-cli/src/command/files.rs | 283 ++++++++++++++++++++------------------ ipfs-cli/src/command/filestore.rs | 103 +++++++------- ipfs-cli/src/command/mod.rs | 118 +++++++++++++++- ipfs-cli/src/command/shutdown.rs | 29 ++-- ipfs-cli/src/command/version.rs | 43 +++--- ipfs-cli/src/main.rs | 82 +++++------ 19 files changed, 942 insertions(+), 744 deletions(-) (limited to 'ipfs-cli') diff --git a/ipfs-cli/Cargo.toml b/ipfs-cli/Cargo.toml index f7fae2a..e0a11e6 100644 --- a/ipfs-cli/Cargo.toml +++ b/ipfs-cli/Cargo.toml @@ -10,5 +10,5 @@ license = "MIT OR Apache-2.0" [dependencies] clap = "2.27" futures = "0.1" +hyper = "0.12" ipfs-api = { path = "../ipfs-api" } -tokio-core = "0.1" diff --git a/ipfs-cli/src/command/add.rs b/ipfs-cli/src/command/add.rs index d732091..4382231 100644 --- a/ipfs-cli/src/command/add.rs +++ b/ipfs-cli/src/command/add.rs @@ -6,28 +6,38 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::{verify_file, EXPECTED_API, EXPECTED_FILE}; -use ipfs_api::IpfsClient; +use clap::App; +use command::{verify_file, CliCommand, EXPECTED_FILE}; +use futures::Future; use std::fs::File; -use tokio_core::reactor::Core; -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand add => - (about: "Add file to IPFS") - (@arg INPUT: +required {verify_file} "File to add") - ) -} +pub struct Command; + +impl CliCommand for Command { + const NAME: &'static str = "add"; + + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand add => + (about: "Add file to IPFS") + (@arg INPUT: +required {verify_file} "File to add") + ) + } -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - let path = args.value_of("INPUT").unwrap(); - let file = File::open(path).expect(EXPECTED_FILE); - let response = core.run(client.add(file)).expect(EXPECTED_API); + handle!( + (args, client) => { + let path = args.value_of("INPUT").unwrap(); + let file = File::open(path).expect(EXPECTED_FILE); - println!(); - println!(" name : {}", response.name); - println!(" hash : {}", response.hash); - println!(" size : {}", response.size); - println!(); + client + .add(file) + .map(|response| { + println!(); + println!(" name : {}", response.name); + println!(" hash : {}", response.hash); + println!(" size : {}", response.size); + println!(); + }) + } + ); } diff --git a/ipfs-cli/src/command/bitswap.rs b/ipfs-cli/src/command/bitswap.rs index a63c21f..95e6752 100644 --- a/ipfs-cli/src/command/bitswap.rs +++ b/ipfs-cli/src/command/bitswap.rs @@ -6,93 +6,106 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::EXPECTED_API; -use ipfs_api::IpfsClient; -use tokio_core::reactor::Core; +use clap::App; +use command::CliCommand; +use futures::Future; -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand bitswap => - (@setting SubcommandRequiredElseHelp) - (@subcommand ledger => - (about: "Show the current ledger for a peer") - (@arg PEER: +required "Peer to inspect") - ) - (@subcommand reprovide => - (about: "Triggers a reprovide") - ) - (@subcommand stat => - (about: "Show some diagnostic information on the bitswap agent") - ) - (@subcommand unwant => - (about: "Remove a given block from your wantlist") - (@arg KEY: +required "Key of the block to remove") - ) - (@subcommand wantlist => - (about: "Shows blocks currently on the wantlist") - ) - ) -} +pub struct Command; -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - match args.subcommand() { - ("ledger", Some(args)) => { - let peer = args.value_of("PEER").unwrap(); - let ledger = core.run(client.bitswap_ledger(peer)).expect(EXPECTED_API); +impl CliCommand for Command { + const NAME: &'static str = "bitswap"; - println!(); - println!(" peer : {}", ledger.peer); - println!(" value : {}", ledger.value); - println!(" sent : {}", ledger.sent); - println!(" recv : {}", ledger.recv); - println!(" exchanged : {}", ledger.exchanged); - println!(); - } - ("reprovide", _) => { - core.run(client.bitswap_reprovide()).expect(EXPECTED_API); - } - ("stat", _) => { - let stat = core.run(client.bitswap_stat()).expect(EXPECTED_API); + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand bitswap => + (@setting SubcommandRequiredElseHelp) + (@subcommand ledger => + (about: "Show the current ledger for a peer") + (@arg PEER: +required "Peer to inspect") + ) + (@subcommand reprovide => + (about: "Triggers a reprovide") + ) + (@subcommand stat => + (about: "Show some diagnostic information on the bitswap agent") + ) + (@subcommand unwant => + (about: "Remove a given block from your wantlist") + (@arg KEY: +required "Key of the block to remove") + ) + (@subcommand wantlist => + (about: "Shows blocks currently on the wantlist") + ) + ) + } - println!(); - println!(" provide_buf_len : {}", stat.provide_buf_len); - println!(" wantlist :"); - for want in stat.wantlist { - println!(" {}", want); - } - println!(" peers :"); - for peer in stat.peers { - println!(" {}", peer); - } - println!(" blocks_received : {}", stat.blocks_received); - println!(" data_received : {}", stat.data_received); - println!(" blocks_sent : {}", stat.blocks_sent); - println!(" data_sent : {}", stat.data_sent); - println!(" dup_blks_received : {}", stat.dup_blks_received); - println!(" dup_data_received : {}", stat.dup_data_received); - println!(); - } - ("unwant", Some(args)) => { - let key = args.value_of("KEY").unwrap(); + handle!( + client; + ("ledger", args) => { + let peer = args.value_of("PEER").unwrap(); - core.run(client.bitswap_unwant(key)).expect(EXPECTED_API); + client + .bitswap_ledger(peer) + .map(|ledger| { + println!(); + println!(" peer : {}", ledger.peer); + println!(" value : {}", ledger.value); + println!(" sent : {}", ledger.sent); + println!(" recv : {}", ledger.recv); + println!(" exchanged : {}", ledger.exchanged); + println!(); + }) + }, + ("reprovide", _args) => { + client.bitswap_reprovide().map(|_| ()) + }, + ("stat", _args) => { + client + .bitswap_stat() + .map(|stat| { + println!(); + println!(" provide_buf_len : {}", stat.provide_buf_len); + println!(" wantlist :"); + for want in stat.wantlist { + println!(" {}", want); + } + println!(" peers :"); + for peer in stat.peers { + println!(" {}", peer); + } + println!(" blocks_received : {}", stat.blocks_received); + println!(" data_received : {}", stat.data_received); + println!(" blocks_sent : {}", stat.blocks_sent); + println!(" data_sent : {}", stat.data_sent); + println!(" dup_blks_received : {}", stat.dup_blks_received); + println!(" dup_data_received : {}", stat.dup_data_received); + println!(); + }) + }, + ("unwant", args) => { + let key = args.value_of("KEY").unwrap(); - println!(); - println!(" OK"); - println!(); - } - ("wantlist", Some(args)) => { + client + .bitswap_unwant(key) + .map(|_| { + println!(); + println!(" OK"); + println!(); + }) + }, + ("wantlist", args) => { let peer = args.value_of("PEER"); - let wantlist = core.run(client.bitswap_wantlist(peer)).expect(EXPECTED_API); - println!(); - println!(" wantlist :"); - for key in wantlist.keys { - println!(" {}", key); - } - println!(); + client + .bitswap_wantlist(peer) + .map(|wantlist| { + println!(); + println!(" wantlist :"); + for key in wantlist.keys { + println!(" {}", key); + } + println!(); + }) } - _ => unreachable!(), - } + ); } diff --git a/ipfs-cli/src/command/block.rs b/ipfs-cli/src/command/block.rs index 7cccb5b..b4ca887 100644 --- a/ipfs-cli/src/command/block.rs +++ b/ipfs-cli/src/command/block.rs @@ -6,77 +6,87 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::{verify_file, EXPECTED_API, EXPECTED_FILE}; -use futures::stream::Stream; -use ipfs_api::IpfsClient; +use clap::App; +use command::{verify_file, CliCommand, EXPECTED_FILE}; +use futures::{Future, Stream}; use std::fs::File; use std::io::{self, Write}; -use tokio_core::reactor::Core; -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand block => - (@setting SubcommandRequiredElseHelp) - (@subcommand get => - (about: "Get a raw IPFS block") - (@arg KEY: +required "The base58 multihash of an existing block") - ) - (@subcommand put => - (about: "Store a file as an IPFS block") - (@arg INPUT: +required {verify_file} "The file to store as an IPFS block") - ) - (@subcommand rm => - (about: "Removes an IPFS block") - (@arg KEY: +required "The base58 multihash of a block to remove") - ) - (@subcommand stat => - (about: "Get information about a raw IPFS block") - (@arg KEY: +required "The base58 multihash of the block to retrieve") - ) - ) -} +pub struct Command; + +impl CliCommand for Command { + const NAME: &'static str = "block"; -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - match args.subcommand() { - ("get", Some(args)) => { + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand block => + (@setting SubcommandRequiredElseHelp) + (@subcommand get => + (about: "Get a raw IPFS block") + (@arg KEY: +required "The base58 multihash of an existing block") + ) + (@subcommand put => + (about: "Store a file as an IPFS block") + (@arg INPUT: +required {verify_file} "The file to store as an IPFS block") + ) + (@subcommand rm => + (about: "Removes an IPFS block") + (@arg KEY: +required "The base58 multihash of a block to remove") + ) + (@subcommand stat => + (about: "Get information about a raw IPFS block") + (@arg KEY: +required "The base58 multihash of the block to retrieve") + ) + ) + } + + handle!( + client; + ("get", args) => { let key = args.value_of("KEY").unwrap(); - let req = client - .block_get(key) - .for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from)); - core.run(req).expect(EXPECTED_API); - } - ("put", Some(args)) => { + client + .block_get(key) + .for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from)) + }, + ("put", args) => { let path = args.value_of("INPUT").unwrap(); let file = File::open(path).expect(EXPECTED_FILE); - let block = core.run(client.block_put(file)).expect(EXPECTED_API); - println!(); - println!(" key : {}", block.key); - println!(" size : {}", block.size); - println!(); - } - ("rm", Some(args)) => { + client + .block_put(file) + .map(|block| { + println!(); + println!(" key : {}", block.key); + println!(" size : {}", block.size); + println!(); + }) + }, + ("rm", args) => { let key = args.value_of("KEY").unwrap(); - let rm = core.run(client.block_rm(key)).expect(EXPECTED_API); - println!(); - println!(" hash : {}", rm.hash); - if let Some(error) = rm.error { - println!(" error : {}", error); - } - println!(); - } - ("stat", Some(args)) => { + client + .block_rm(key) + .map(|rm| { + println!(); + println!(" hash : {}", rm.hash); + if let Some(error) = rm.error { + println!(" error : {}", error); + } + println!(); + }) + }, + ("stat", args) => { let key = args.value_of("KEY").unwrap(); - let stat = core.run(client.block_stat(key)).expect(EXPECTED_API); - println!(); - println!(" key : {}", stat.key); - println!(" size : {}", stat.size); - println!(); + client + .block_stat(key) + .map(|stat| { + println!(); + println!(" key : {}", stat.key); + println!(" size : {}", stat.size); + println!(); + }) } - _ => unreachable!(), - } + ); } diff --git a/ipfs-cli/src/command/bootstrap.rs b/ipfs-cli/src/command/bootstrap.rs index dc3014e..af1e406 100644 --- a/ipfs-cli/src/command/bootstrap.rs +++ b/ipfs-cli/src/command/bootstrap.rs @@ -6,32 +6,9 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::EXPECTED_API; -use ipfs_api::IpfsClient; -use tokio_core::reactor::Core; - -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand bootstrap => - (@setting SubcommandRequiredElseHelp) - (@subcommand add => - (@setting SubcommandRequiredElseHelp) - (@subcommand default => - (about: "Add default peers to the bootstrap list") - ) - ) - (@subcommand list => - (about: "Show peers in the bootstrap list") - ) - (@subcommand rm => - (@setting SubcommandRequiredElseHelp) - (@subcommand all => - (about: "Remove all peers from the bootstrap list") - ) - ) - ) -} +use clap::App; +use command::CliCommand; +use futures::Future; fn print_peers(peers: &[String]) { println!(); @@ -42,30 +19,47 @@ fn print_peers(peers: &[String]) { println!(); } -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - match args.subcommand() { - ("add", Some(add)) => match add.subcommand() { - ("default", _) => { - let peers = core.run(client.bootstrap_add_default()) - .expect(EXPECTED_API); +pub struct Command; - print_peers(&peers.peers); - } - _ => unreachable!(), - }, - ("list", _) => { - let peers = core.run(client.bootstrap_list()).expect(EXPECTED_API); +impl CliCommand for Command { + const NAME: &'static str = "bootstrap"; - print_peers(&peers.peers); - } - ("rm", Some(rm)) => match rm.subcommand() { - ("all", _) => { - let peers = core.run(client.bootstrap_rm_all()).expect(EXPECTED_API); + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand bootstrap => + (@setting SubcommandRequiredElseHelp) + (@subcommand add => + (@setting SubcommandRequiredElseHelp) + (@subcommand default => + (about: "Add default peers to the bootstrap list") + ) + ) + (@subcommand list => + (about: "Show peers in the bootstrap list") + ) + (@subcommand rm => + (@setting SubcommandRequiredElseHelp) + (@subcommand all => + (about: "Remove all peers from the bootstrap list") + ) + ) + ) + } - print_peers(&peers.peers); + handle!( + client; + ("add") => { + ("default", _args) => { + client.bootstrap_add_default().map(|peers| print_peers(&peers.peers)) } - _ => unreachable!(), }, - _ => unreachable!(), - } + ("list", _args) => { + client.bootstrap_list().map(|peers| print_peers(&peers.peers)) + }, + ("rm") => { + ("all", _args) => { + client.bootstrap_rm_all().map(|peers| print_peers(&peers.peers)) + } + } + ); } diff --git a/ipfs-cli/src/command/cat.rs b/ipfs-cli/src/command/cat.rs index 1f77306..6e8a9e7 100644 --- a/ipfs-cli/src/command/cat.rs +++ b/ipfs-cli/src/command/cat.rs @@ -6,26 +6,31 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::EXPECTED_API; -use futures::stream::Stream; -use ipfs_api::IpfsClient; +use clap::App; +use command::CliCommand; +use futures::{Future, Stream}; use std::io::{self, Write}; -use tokio_core::reactor::Core; -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand cat => - (about: "Show IPFS object data") - (@arg PATH: +required "The path of the IPFS object to get") - ) -} +pub struct Command; + +impl CliCommand for Command { + const NAME: &'static str = "cat"; + + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand cat => + (about: "Show IPFS object data") + (@arg PATH: +required "The path of the IPFS object to get") + ) + } -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - let path = args.value_of("PATH").unwrap(); - let req = client - .cat(&path) - .for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from)); + handle!( + (args, client) => { + let path = args.value_of("PATH").unwrap(); - core.run(req).expect(EXPECTED_API); + client + .cat(&path) + .for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from)) + } + ); } diff --git a/ipfs-cli/src/command/commands.rs b/ipfs-cli/src/command/commands.rs index ffba5f2..4ded502 100644 --- a/ipfs-cli/src/command/commands.rs +++ b/ipfs-cli/src/command/commands.rs @@ -1,5 +1,4 @@ // Copyright 2017 rust-ipfs-api Developers -// // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be @@ -7,17 +6,9 @@ // use clap::App; -use command::EXPECTED_API; -use ipfs_api::IpfsClient; +use command::CliCommand; +use futures::Future; use ipfs_api::response::CommandsResponse; -use tokio_core::reactor::Core; - -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand commands => - (about: "List all available commands") - ) -} fn recursive_print_commands(cmd: CommandsResponse, stack: &mut Vec) { if cmd.subcommands.is_empty() { @@ -35,10 +26,25 @@ fn recursive_print_commands(cmd: CommandsResponse, stack: &mut Vec) { } } -pub fn handle(core: &mut Core, client: &IpfsClient) { - let commands = core.run(client.commands()).expect(EXPECTED_API); +pub struct Command; + +impl CliCommand for Command { + const NAME: &'static str = "commands"; - println!(); - recursive_print_commands(commands, &mut Vec::new()); - println!(); + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand commands => + (about: "List all available commands") + ) + } + + handle!( + (_args, client) => { + client.commands().map(|commands| { + println!(); + recursive_print_commands(commands, &mut Vec::new()); + println!(); + }) + } + ); } diff --git a/ipfs-cli/src/command/config.rs b/ipfs-cli/src/command/config.rs index e778816..1b0cf79 100644 --- a/ipfs-cli/src/command/config.rs +++ b/ipfs-cli/src/command/config.rs @@ -6,43 +6,46 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::{verify_file, EXPECTED_API, EXPECTED_FILE}; -use ipfs_api::IpfsClient; +use clap::App; +use command::{verify_file, CliCommand, EXPECTED_FILE}; +use futures::Future; use std::fs::File; -use tokio_core::reactor::Core; -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand config => - (@setting SubcommandRequiredElseHelp) - (@subcommand edit => - (about: "Open the config file for editing") - ) - (@subcommand replace => - (about: "Replace the config file") - (@arg INPUT: +required {verify_file} "Config file to replace with") - ) - (@subcommand show => - (about: "Show the config file") - ) - ) -} +pub struct Command; + +impl CliCommand for Command { + const NAME: &'static str = "config"; + + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand config => + (@setting SubcommandRequiredElseHelp) + (@subcommand edit => + (about: "Open the config file for editing") + ) + (@subcommand replace => + (about: "Replace the config file") + (@arg INPUT: +required {verify_file} "Config file to replace with") + ) + (@subcommand show => + (about: "Show the config file") + ) + ) + } -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - match args.subcommand() { - ("edit", _) => core.run(client.config_edit()).expect(EXPECTED_API), - ("replace", Some(args)) => { + handle!( + client; + ("edit", _args) => { + client.config_edit().map(|_| ()) + }, + ("replace", args) => { let path = args.value_of("INPUT").unwrap(); let config = File::open(path).expect(EXPECTED_FILE); - core.run(client.config_replace(config)).expect(EXPECTED_API); + client.config_replace(config).map(|_| ()) + }, + ("show", _args) => { + client.config_show().map(|config| println!("{}", config)) } - ("show", _) => { - let config = core.run(client.config_show()).expect(EXPECTED_API); - - println!("{}", config); - } - _ => unreachable!(), - } + ); } diff --git a/ipfs-cli/src/command/dag.rs b/ipfs-cli/src/command/dag.rs index c2b4f39..cd070ac 100644 --- a/ipfs-cli/src/command/dag.rs +++ b/ipfs-cli/src/command/dag.rs @@ -6,39 +6,43 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::EXPECTED_API; -use ipfs_api::IpfsClient; -use tokio_core::reactor::Core; +use clap::App; +use command::CliCommand; +use futures::Future; -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand dag => - (@setting SubcommandRequiredElseHelp) - (@subcommand get => - (about: "Get a dag node from IPFS") - (@arg KEY: +required "The key of the object to get") - ) - ) -} +pub struct Command; + +impl CliCommand for Command { + const NAME: &'static str = "dag"; -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - match args.subcommand() { - ("get", Some(args)) => { + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand dag => + (@setting SubcommandRequiredElseHelp) + (@subcommand get => + (about: "Get a dag node from IPFS") + (@arg KEY: +required "The key of the object to get") + ) + ) + } + + handle!( + client; + ("get", args) => { let key = args.value_of("KEY").unwrap(); - let dag = core.run(client.dag_get(key)).expect(EXPECTED_API); - println!(); - if let Some(data) = dag.data { - println!(" data :"); - println!("{}", data); - } - println!(" links :"); - for link in dag.links { - println!(" {} ({}) ({:?})", link.name, link.size, link.cid); - } - println!(); + client.dag_get(key).map(|dag| { + println!(); + if let Some(data) = dag.data { + println!(" data :"); + println!("{}", data); + } + println!(" links :"); + for link in dag.links { + println!(" {} ({}) ({:?})", link.name, link.size, link.cid); + } + println!(); + }) } - _ => unreachable!(), - } + ); } diff --git a/ipfs-cli/src/command/dht.rs b/ipfs-cli/src/command/dht.rs index 9f95d3a..0e13f22 100644 --- a/ipfs-cli/src/command/dht.rs +++ b/ipfs-cli/src/command/dht.rs @@ -6,44 +6,10 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::EXPECTED_API; -use futures::stream::Stream; -use ipfs_api::IpfsClient; +use clap::App; +use command::CliCommand; +use futures::{Future, Stream}; use ipfs_api::response::DhtMessage; -use tokio_core::reactor::Core; - -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand dht => - (@setting SubcommandRequiredElseHelp) - (@subcommand findpeer => - (about: "Query the DHT for all of the multiaddresses associated with a Peer ID") - (@arg PEER: +required "Peer to search for") - ) - (@subcommand findprovs => - (about: "Find peers in the DHT that can provide the given key") - (@arg KEY: +required "Key to search for") - ) - (@subcommand get => - (about: "Given a key, query the DHT for its best value") - (@arg KEY: +required "The key search for") - ) - (@subcommand provide => - (about: "Announce to the network that you are providing the given values") - (@arg KEY: +required "The key you are providing") - ) - (@subcommand put => - (about: "Write a key/value pair to the DHT") - (@arg KEY: +required "The key to store the value at") - (@arg VALUE: +required "The value to store") - ) - (@subcommand query => - (about: "Find the closest peer to a given peer by querying the DHT") - (@arg PEER: +required "The peer to run the query against") - ) - ) -} fn print_dht_response(res: DhtMessage) -> Result<(), E> { println!(); @@ -64,42 +30,75 @@ fn print_dht_response(res: DhtMessage) -> Result<(), E> { Ok(()) } -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - let req = match args.subcommand() { - ("findpeer", Some(args)) => { +pub struct Command; + +impl CliCommand for Command { + const NAME: &'static str = "dht"; + + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand dht => + (@setting SubcommandRequiredElseHelp) + (@subcommand findpeer => + (about: "Query the DHT for all of the multiaddresses associated with a Peer ID") + (@arg PEER: +required "Peer to search for") + ) + (@subcommand findprovs => + (about: "Find peers in the DHT that can provide the given key") + (@arg KEY: +required "Key to search for") + ) + (@subcommand get => + (about: "Given a key, query the DHT for its best value") + (@arg KEY: +required "The key search for") + ) + (@subcommand provide => + (about: "Announce to the network that you are providing the given values") + (@arg KEY: +required "The key you are providing") + ) + (@subcommand put => + (about: "Write a key/value pair to the DHT") + (@arg KEY: +required "The key to store the value at") + (@arg VALUE: +required "The value to store") + ) + (@subcommand query => + (about: "Find the closest peer to a given peer by querying the DHT") + (@arg PEER: +required "The peer to run the query against") + ) + ) + } + + handle!( + client; + ("findpeer", args) => { let peer = args.value_of("PEER").unwrap(); - client.dht_findpeer(peer) - } - ("findprovs", Some(args)) => { + client.dht_findpeer(peer).for_each(print_dht_response) + }, + ("findprovs", args) => { let key = args.value_of("KEY").unwrap(); - client.dht_findprovs(key) - } - ("get", Some(args)) => { + client.dht_findprovs(key).for_each(print_dht_response) + }, + ("get", args) => { let key = args.value_of("KEY").unwrap(); - client.dht_get(key) - } - ("provide", Some(args)) => { + client.dht_get(key).for_each(print_dht_response) + }, + ("provide", args) => { let key = args.value_of("KEY").unwrap(); - client.dht_provide(&key) - } - ("put", Some(args)) => { + client.dht_provide(&key).for_each(print_dht_response) + }, + ("put", args) => { let key = args.value_of("KEY").unwrap(); let val = args.value_of("VALUE").unwrap(); - client.dht_put(key, val) - } - ("query", Some(args)) => { + client.dht_put(key, val).for_each(print_dht_response) + }, + ("query", args) => { let peer = args.value_of("PEER").unwrap(); - client.dht_query(peer) + client.dht_query(peer).for_each(print_dht_response) } - _ => unreachable!(), - }; - - core.run(req.for_each(print_dht_response)) - .expect(EXPECTED_API); + ); } diff --git a/ipfs-cli/src/command/diag.rs b/ipfs-cli/src/command/diag.rs index 7ce6c22..1c5dee9 100644 --- a/ipfs-cli/src/command/diag.rs +++ b/ipfs-cli/src/command/diag.rs @@ -6,69 +6,76 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, Arg, ArgMatches, SubCommand}; -use command::EXPECTED_API; -use ipfs_api::IpfsClient; -use tokio_core::reactor::Core; +use clap::{App, Arg, SubCommand}; +use command::CliCommand; +use futures::Future; -pub fn signature<'a, 'b>() -> App<'a, 'b> { - // The clap macro does not allow hyphenated command names, - // so the `set-time` command has to be manually instantiated. - // - let set_time_command = SubCommand::with_name("set-time") - .about("Set how long to keep inactive requests in the log") - .arg( - Arg::with_name("TIME") - .required(true) - .index(1) - .help("Time to keep inactive requests in the log"), - ); +pub struct Command; - clap_app!( - @subcommand diag => - (@setting SubcommandRequiredElseHelp) - (@subcommand cmds => +impl CliCommand for Command { + const NAME: &'static str = "diag"; + + fn signature<'a, 'b>() -> App<'a, 'b> { + // The clap macro does not allow hyphenated command names, + // so the `set-time` command has to be manually instantiated. + // + let set_time_command = SubCommand::with_name("set-time") + .about("Set how long to keep inactive requests in the log") + .arg( + Arg::with_name("TIME") + .required(true) + .index(1) + .help("Time to keep inactive requests in the log"), + ); + + clap_app!( + @subcommand diag => (@setting SubcommandRequiredElseHelp) - (@subcommand clear => - (about: "Clear inactive requests from the log") + (@subcommand cmds => + (@setting SubcommandRequiredElseHelp) + (@subcommand clear => + (about: "Clear inactive requests from the log") + ) + (subcommand: set_time_command) ) - (subcommand: set_time_command) - ) - (@subcommand sys => - (about: "Print system diagnostic information") - ) - ) -} - -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - match args.subcommand() { - ("cmds", Some(args)) => match args.subcommand() { - ("clear", _) => { - core.run(client.diag_cmds_clear()).expect(EXPECTED_API); + (@subcommand sys => + (about: "Print system diagnostic information") + ) + ) + } - println!(""); - println!(" OK"); - println!(""); - } - ("set-time", Some(args)) => { + handle!( + client; + ("cmds") => { + ("clear", _args) => { + client + .diag_cmds_clear() + .map(|_| { + println!(""); + println!(" OK"); + println!(""); + }) + }, + ("set-time", args) => { let time = args.value_of("TIME").unwrap(); - core.run(client.diag_cmds_set_time(&time)) - .expect(EXPECTED_API); - - println!(""); - println!(" OK"); - println!(""); + client + .diag_cmds_set_time(&time) + .map(|_| { + println!(""); + println!(" OK"); + println!(""); + }) } - _ => unreachable!(), }, - ("sys", _) => { - let sys = core.run(client.diag_sys()).expect(EXPECTED_API); - - println!(); - println!(" {}", sys); - println!(); + ("sys", _args) => { + client + .diag_sys() + .map(|sys| { + println!(); + println!(" {}", sys); + println!(); + }) } - _ => unreachable!(), - } + ); } diff --git a/ipfs-cli/src/command/dns.rs b/ipfs-cli/src/command/dns.rs index ba5ab94..5f60d09 100644 --- a/ipfs-cli/src/command/dns.rs +++ b/ipfs-cli/src/command/dns.rs @@ -6,26 +6,35 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::EXPECTED_API; -use ipfs_api::IpfsClient; -use tokio_core::reactor::Core; +use clap::App; +use command::CliCommand; +use futures::Future; -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand dns => - (about: "Resolve a DNS link") - (@arg PATH: +required "The domain name to resolve") - (@arg recursive: -r --recursive "Resolve until the result is not a DNS link") - ) -} +pub struct Command; + +impl CliCommand for Command { + const NAME: &'static str = "dns"; + + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand dns => + (about: "Resolve a DNS link") + (@arg PATH: +required "The domain name to resolve") + (@arg recursive: -r --recursive "Resolve until the result is not a DNS link") + ) + } -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - let path = args.value_of("PATH").unwrap(); - let req = client.dns(path, args.is_present("recursive")); - let res = core.run(req).expect(EXPECTED_API); + handle!( + (args, client) => { + let path = args.value_of("PATH").unwrap(); - println!(); - println!(" path : {}", res.path); - println!(); + client + .dns(path, args.is_present("recursive")) + .map(|res| { + println!(); + println!(" path : {}", res.path); + println!(); + }) + } + ); } diff --git a/ipfs-cli/src/command/file.rs b/ipfs-cli/src/command/file.rs index 9fd884a..7f6aeb6 100644 --- a/ipfs-cli/src/command/file.rs +++ b/ipfs-cli/src/command/file.rs @@ -6,54 +6,60 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::EXPECTED_API; -use ipfs_api::IpfsClient; -use tokio_core::reactor::Core; +use clap::App; +use command::CliCommand; +use futures::Future; -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand file => - (@setting SubcommandRequiredElseHelp) - (@subcommand ls => - (about: "List directory contents for Unix filesystem objects") - (@arg PATH: +required "THe path to list links from") - ) - ) -} +pub struct Command; + +impl CliCommand for Command { + const NAME: &'static str = "file"; + + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand file => + (@setting SubcommandRequiredElseHelp) + (@subcommand ls => + (about: "List directory contents for Unix filesystem objects") + (@arg PATH: +required "The path to list links from") + ) + ) + } -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - match args.subcommand() { - ("ls", Some(args)) => { + handle!( + client; + ("ls", args) => { let path = args.value_of("PATH").unwrap(); - let ls = core.run(client.file_ls(path)).expect(EXPECTED_API); - println!(); - println!(" arguments :"); - for (k, arg) in ls.arguments { - println!(" arg : {}", k); - println!(" value : {}", arg); - println!(); - } - println!(" objects :"); - for (k, obj) in ls.objects { - println!(" key : {}", k); - println!(" hash : {}", obj.hash); - println!(" size : {}", obj.size); - println!(" type : {}", obj.typ); - println!(" links :"); - for link in obj.links { - println!(" name : {}", link.name); - println!(" hash : {}", link.hash); - println!(" size : {}", link.size); - if let Some(ref typ) = link.typ { - println!(" type : {}", typ); + client + .file_ls(path) + .map(|ls| { + println!(); + println!(" arguments :"); + for (k, arg) in ls.arguments { + println!(" arg : {}", k); + println!(" value : {}", arg); + println!(); + } + println!(" objects :"); + for (k, obj) in ls.objects { + println!(" key : {}", k); + println!(" hash : {}", obj.hash); + println!(" size : {}", obj.size); + println!(" type : {}", obj.typ); + println!(" links :"); + for link in obj.links { + println!(" name : {}", link.name); + println!(" hash : {}", link.hash); + println!(" size : {}", link.size); + if let Some(ref typ) = link.typ { + println!(" type : {}", typ); + } + println!(); + } } println!(); - } - } - println!(); + }) } - _ => unreachable!(), - } + ); } diff --git a/ipfs-cli/src/command/files.rs b/ipfs-cli/src/command/files.rs index d3d0d75..3f1165e 100644 --- a/ipfs-cli/src/command/files.rs +++ b/ipfs-cli/src/command/files.rs @@ -6,168 +6,181 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::{verify_file, EXPECTED_API, EXPECTED_FILE}; -use futures::stream::Stream; -use ipfs_api::IpfsClient; +use clap::App; +use command::{verify_file, CliCommand, EXPECTED_FILE}; +use futures::{Future, Stream}; use std::fs::File; use std::io::{self, Write}; -use tokio_core::reactor::Core; -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand files => - (@setting SubcommandRequiredElseHelp) - (@subcommand cp => - (about: "Copy files in MFS") - (@arg SRC: +required "The source object to copy") - (@arg DEST: +required "The destination to copy the object to") - ) - (@subcommand flush => - (about: "Flush a path's data to disk") - (@arg PATH: "The path to flush") - ) - (@subcommand ls => - (about: "List directories in MFS") - (@arg PATH: "The past to list") - ) - (@subcommand mkdir => - (about: "Make directories in MFS") - (@arg PATH: +required "The directory to create") - (@arg parents: -p --parents "Create parents if the directory \ - does not already exist") - ) - (@subcommand mv => - (about: "Move files in MFS") - (@arg SRC: +required "The source object to move") - (@arg DEST: +required "The destination to move the object to") - ) - (@subcommand read => - (about: "Read a file in MFS") - (@arg PATH: +required "The path to read") - ) - (@subcommand rm => - (about: "Remove a file in MFS") - (@arg PATH: +required "The file to remove") - (@arg recursive: -r --recursive "Recursively remove directories") - ) - (@subcommand stat => - (about: "Display status for a file in MFS") - (@arg PATH: +required "The file to get status for") - ) - (@subcommand write => - (about: "Write a file to MFS") - (@arg DEST: +required "The path to write to") - (@arg INPUT: +required {verify_file} "The file to write") - (@arg create: --create "Create the file if it does not exist") - (@arg truncate: --truncate "Truncate the file before writing") - ) - ) -} +pub struct Command; + +impl CliCommand for Command { + const NAME: &'static str = "files"; + + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand files => + (@setting SubcommandRequiredElseHelp) + (@subcommand cp => + (about: "Copy files in MFS") + (@arg SRC: +required "The source object to copy") + (@arg DEST: +required "The destination to copy the object to") + ) + (@subcommand flush => + (about: "Flush a path's data to disk") + (@arg PATH: "The path to flush") + ) + (@subcommand ls => + (about: "List directories in MFS") + (@arg PATH: "The past to list") + ) + (@subcommand mkdir => + (about: "Make directories in MFS") + (@arg PATH: +required "The directory to create") + (@arg parents: -p --parents "Create parents if the directory \ + does not already exist") + ) + (@subcommand mv => + (about: "Move files in MFS") + (@arg SRC: +required "The source object to move") + (@arg DEST: +required "The destination to move the object to") + ) + (@subcommand read => + (about: "Read a file in MFS") + (@arg PATH: +required "The path to read") + ) + (@subcommand rm => + (about: "Remove a file in MFS") + (@arg PATH: +required "The file to remove") + (@arg recursive: -r --recursive "Recursively remove directories") + ) + (@subcommand stat => + (about: "Display status for a file in MFS") + (@arg PATH: +required "The file to get status for") + ) + (@subcommand write => + (about: "Write a file to MFS") + (@arg DEST: +required "The path to write to") + (@arg INPUT: +required {verify_file} "The file to write") + (@arg create: --create "Create the file if it does not exist") + (@arg truncate: --truncate "Truncate the file before writing") + ) + ) + } -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - match args.subcommand() { - ("cp", Some(args)) => { + handle!( + client; + ("cp", args) => { let src = args.value_of("SRC").unwrap(); let dest = args.value_of("DEST").unwrap(); - core.run(client.files_cp(src, dest)).expect(EXPECTED_API); - - println!(); - println!(" OK"); - println!(); - } - ("flush", Some(args)) => { + client + .files_cp(src, dest) + .map(|_| { + println!(); + println!(" OK"); + println!(); + }) + }, + ("flush", args) => { let path = args.value_of("PATH"); - core.run(client.files_flush(path)).expect(EXPECTED_API); - - println!(); - println!(" OK"); - println!(); - } - ("ls", Some(args)) => { + client + .files_flush(path) + .map(|_| { + println!(); + println!(" OK"); + println!(); + }) + }, + ("ls", args) => { let path = args.value_of("PATH"); - let ls = core.run(client.files_ls(path)).expect(EXPECTED_API); - println!(); - println!(" entries :"); - for entry in ls.entries { - println!(" name : {}", entry.name); - println!(" type : {}", entry.typ); - println!(" size : {}", entry.size); - println!(" hash : {}", entry.hash); - println!(); - } - println!(); - } - ("mkdir", Some(args)) => { + client + .files_ls(path) + .map(|ls| { + println!(); + println!(" entries :"); + for entry in ls.entries { + println!(" name : {}", entry.name); + println!(" type : {}", entry.typ); + println!(" size : {}", entry.size); + println!(" hash : {}", entry.hash); + println!(); + } + println!(); + }) + }, + ("mkdir", args) => { let path = args.value_of("PATH").unwrap(); - core.run(client.files_mkdir(path, args.is_present("parents"))) - .expect(EXPECTED_API); - - println!(); - println!(" OK"); - println!(); - } - ("mv", Some(args)) => { + client + .files_mkdir(path, args.is_present("parents")) + .map(|_| { + println!(); + println!(" OK"); + println!(); + }) + }, + ("mv", args) => { let src = args.value_of("SRC").unwrap(); let dest = args.value_of("DEST").unwrap(); - core.run(client.files_mv(src, dest)).expect(EXPECTED_API); - - println!(); - println!(" OK"); - println!(); - } - ("read", Some(args)) => { + client + .files_mv(src, dest) + .map(|_| { + println!(); + println!(" OK"); + println!(); + }) + }, + ("read", args) => { let path = args.value_of("PATH").unwrap(); - let req = client - .files_read(path) - .for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from)); - core.run(req).expect(EXPECTED_API); - } - ("rm", Some(args)) => { + client + .files_read(path) + .for_each(|chunk| io::stdout().write_all(&chunk).map_err(From::from)) + }, + ("rm", args) => { let path = args.value_of("PATH").unwrap(); - let req = client.files_rm(path, args.is_present("recursive")); - - core.run(req).expect(EXPECTED_API); - println!(); - println!(" OK"); - println!(); - } - ("stat", Some(args)) => { + client + .files_rm(path, args.is_present("recursive")) + .map(|_| { + println!(); + println!(" OK"); + println!(); + }) + }, + ("stat", args) => { let path = args.value_of("PATH").unwrap(); - let stat = core.run(client.files_stat(path)).expect(EXPECTED_API); - - println!(); - println!(" hash : {}", stat.hash); - println!(" size : {}", stat.size); - println!(" cumulative_size : {}", stat.cumulative_size); - println!(" blocks : {}", stat.blocks); - println!(" type : {}", stat.typ); - println!(); - } - ("write", Some(args)) => { + client + .files_stat(path) + .map(|stat| { + println!(); + println!(" hash : {}", stat.hash); + println!(" size : {}", stat.size); + println!(" cumulative_size : {}", stat.cumulative_size); + println!(" blocks : {}", stat.blocks); + println!(" type : {}", stat.typ); + println!(); + }) + }, + ("write", args) => { let dest = args.value_of("DEST").unwrap(); let path = args.value_of("INPUT").unwrap(); let file = File::open(path).expect(EXPECTED_FILE); - let req = client.files_write( + + client.files_write( dest, args.is_present("create"), args.is_present("truncate"), file, - ); - - core.run(req).expect(EXPECTED_API); - - println!(); - println!(" OK"); - println!(); + ).map(|_| { + println!(); + println!(" OK"); + println!(); + }) } - _ => unreachable!(), - } + ); } diff --git a/ipfs-cli/src/command/filestore.rs b/ipfs-cli/src/command/filestore.rs index bf49c09..f8a275f 100644 --- a/ipfs-cli/src/command/filestore.rs +++ b/ipfs-cli/src/command/filestore.rs @@ -6,31 +6,12 @@ // copied, modified, or distributed except according to those terms. // -use clap::{App, ArgMatches}; -use command::EXPECTED_API; -use futures::stream::Stream; -use ipfs_api::{response, IpfsClient}; -use tokio_core::reactor::Core; +use clap::App; +use command::CliCommand; +use futures::{Future, Stream}; +use ipfs_api::response::FilestoreObject; -pub fn signature<'a, 'b>() -> App<'a, 'b> { - clap_app!( - @subcommand filestore => - (@setting SubcommandRequiredElseHelp) - (@subcommand dups => - (about: "List blocks that are both in the filestore and standard block storage") - ) - (@subcommand ls => - (about: "List objects in the filestore") - (@arg CID: "Cid of the object to list") - ) - (@subcommand verify => - (about: "Verify objects in the filestore") - (@arg CID: "Cid of the object to verify") - ) - ) -} - -fn print_filestore_object(obj: &response::FilestoreObject) -> Result<(), E> { +fn print_filestore_object(obj: FilestoreObject) -> Result<(), E> { println!(" status : {}", obj.status); println!(" error_msg : {}", obj.error_msg); println!(" key : {}", obj.key); @@ -42,41 +23,61 @@ fn print_filestore_object(obj: &response::FilestoreObject) -> Result<(), E> { Ok(()) } -pub fn handle(core: &mut Core, client: &IpfsClient, args: &ArgMatches) { - match args.subcommand() { - ("dups", _) => { - let req = client.filestore_dups().for_each(|dup| { - println!(" ref : {}", dup.reference); - println!(" err : {}", dup.err); - println!(); +pub struct Command; - Ok(()) - }); +impl CliCommand for Command { + const NAME: &'static str = "filestore"; + fn signature<'a, 'b>() -> App<'a, 'b> { + clap_app!( + @subcommand filestore => + (@setting SubcommandRequiredElseHelp) + (@subcommand dups => + (about: "List blocks that are both in the filestore and standard block storage") + ) + (@subcommand ls => + (about: "List objects in the filestore") + (@arg CID: "Cid of the object to list") + ) + (@subcommand verify => + (about: "Verify objects in the filestore") + (@arg CID: "Cid of the object to verify") + ) + ) + } + + handle!( + client; + ("dups", _args) => { println!(); - core.run(req).expect(EXPECTED_API); - println!(); - } - ("ls", Some(args)) => { + + client + .filestore_dups() + .for_each(|dup| { + println!(" ref : {}", dup.reference); + println!(" err : {}", dup.err); + println!(); + + Ok(()) + }) + }, + ("ls", args) => { let cid = args.value_of("CID"); - let req = client - .filestore_ls(cid) - .for_each(|res| print_filestore_object(&res)); println!(); - core.run(req).expect(EXPECTED_API); - println!(); - } - ("verify", Some(args)) => { + + client + .filestore_ls(cid) + .for_each(print_filestore_object) + }, + ("verify", args) => { let cid = args.value_of("CID"); - let req = client - .filestore_verify(cid) - .for_each(|obj| print_filestore_object(&obj)); println!(); - core.run(req).expect(EXPECTED_API); - println!(); + + client + .filestore_verify(cid) + .for_each(print_filestore_object) } - _ => unreachable!(), - } + ); } 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 + '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; diff --git a/ipfs-cli/src/command/shutdown.rs b/ipfs-cli/src/command/shutdown.rs index 6cb9c1d..e32b7f1 100644 --- a/ipfs-cli/src/command/shutdown.rs +++ b/ipfs-cli/src/comman