clidispatch: initial migration to define_flags!

Summary:
Use `define_flags!` to auto generate conversion from `ParseOutput`.
A side effect is `args` is moved to the struct, and function signature becomes simpler.

Currently, `args` will also include the command name itself. This might be
useful when multiple commands share a similar implementation (ex. `next`, `prev`).

Reviewed By: sfilipco

Differential Revision: D16733269

fbshipit-source-id: fe32e41fe48a97d2d2f5a122522a17fa3c5f5f82
This commit is contained in:
Jun Wu 2019-08-19 19:24:40 -07:00 committed by Facebook Github Bot
parent 76cb250fcd
commit 8ec9e492ff
3 changed files with 43 additions and 66 deletions

View File

@ -8,11 +8,9 @@ use crate::repo::Repo;
use cliparser::parser::{Flag, ParseOutput};
pub enum CommandType {
NoRepo(Box<dyn Fn(ParseOutput, Vec<String>, &mut IO) -> Result<u8, DispatchError>>),
InferRepo(
Box<dyn Fn(ParseOutput, Vec<String>, &mut IO, Option<Repo>) -> Result<u8, DispatchError>>,
),
Repo(Box<dyn Fn(ParseOutput, Vec<String>, &mut IO, Repo) -> Result<u8, DispatchError>>),
NoRepo(Box<dyn Fn(ParseOutput, &mut IO) -> Result<u8, DispatchError>>),
InferRepo(Box<dyn Fn(ParseOutput, &mut IO, Option<Repo>) -> Result<u8, DispatchError>>),
Repo(Box<dyn Fn(ParseOutput, &mut IO, Repo) -> Result<u8, DispatchError>>),
}
pub struct CommandDefinition {

View File

@ -413,8 +413,6 @@ impl Dispatcher {
Dispatcher::last_chance_to_abort(&result)?;
let command_length = command_name.split(" ").count();
let handler = self.command_table.get(&command_name).unwrap();
let res = match handler {
@ -427,8 +425,7 @@ impl Dispatcher {
})?;
r.set_config(config_set);
let args = result.args().iter().skip(command_length).cloned().collect();
f(result, args, io, r)
f(result, io, r)
}
CommandType::InferRepo(f) => {
let r = match repo {
@ -438,13 +435,9 @@ impl Dispatcher {
}
None => None,
};
let args = args.iter().skip(command_length).cloned().collect();
f(result, args, io, r)
}
CommandType::NoRepo(f) => {
let args = result.args().clone();
f(result, args, io)
f(result, io, r)
}
CommandType::NoRepo(f) => f(result, io),
};
res
@ -458,13 +451,13 @@ pub trait Register<FN, T> {
// No Repo
impl<S, FN> Register<FN, (S,)> for Dispatcher
where
S: From<ParseOutput>,
FN: Fn(S, Vec<String>, &mut IO) -> Result<u8, DispatchError> + 'static,
S: From<ParseOutput> + StructFlags,
FN: Fn(S, &mut IO) -> Result<u8, DispatchError> + 'static,
{
fn register(&mut self, command: CommandDefinition, inner_func: FN) {
let wrapped = move |opts: ParseOutput, args: Vec<String>, io: &mut IO| {
let wrapped = move |opts: ParseOutput, io: &mut IO| {
let translated_opts = opts.into();
inner_func(translated_opts, args, io)
inner_func(translated_opts, io)
};
self.command_table.insert(
command.name().to_owned(),
@ -477,15 +470,14 @@ where
// Infer Repo
impl<S, FN> Register<FN, ((), (((S,),),))> for Dispatcher
where
S: From<ParseOutput>,
FN: Fn(S, Vec<String>, &mut IO, Option<Repo>) -> Result<u8, DispatchError> + 'static,
S: From<ParseOutput> + StructFlags,
FN: Fn(S, &mut IO, Option<Repo>) -> Result<u8, DispatchError> + 'static,
{
fn register(&mut self, command: CommandDefinition, inner_func: FN) {
let wrapped =
move |opts: ParseOutput, args: Vec<String>, io: &mut IO, repo: Option<Repo>| {
let translated_opts = opts.into();
inner_func(translated_opts, args, io, repo)
};
let wrapped = move |opts: ParseOutput, io: &mut IO, repo: Option<Repo>| {
let translated_opts = opts.into();
inner_func(translated_opts, io, repo)
};
self.command_table.insert(
command.name().to_owned(),
CommandType::InferRepo(Box::new(wrapped)),
@ -497,13 +489,13 @@ where
// Repo
impl<S, FN> Register<FN, ((), (), ((S,),))> for Dispatcher
where
S: From<ParseOutput>,
FN: Fn(S, Vec<String>, &mut IO, Repo) -> Result<u8, DispatchError> + 'static,
S: From<ParseOutput> + StructFlags,
FN: Fn(S, &mut IO, Repo) -> Result<u8, DispatchError> + 'static,
{
fn register(&mut self, command: CommandDefinition, inner_func: FN) {
let wrapped = move |opts: ParseOutput, args: Vec<String>, io: &mut IO, repo: Repo| {
let wrapped = move |opts: ParseOutput, io: &mut IO, repo: Repo| {
let translated_opts = opts.into();
inner_func(translated_opts, args, io, repo)
inner_func(translated_opts, io, repo)
};
self.command_table.insert(
command.name().to_owned(),

View File

@ -5,7 +5,8 @@ use clidispatch::dispatch::*;
use clidispatch::errors::DispatchError;
use clidispatch::io::IO;
use clidispatch::repo::Repo;
use cliparser::parser::ParseOutput;
use cliparser::define_flags;
use cliparser::parser::StructFlags;
use revisionstore::{DataPackStore, DataStore, IndexedLogDataStore, UnionDataStore};
use types::{Key, Node, RepoPathBuf};
@ -30,7 +31,7 @@ pub fn dispatch(dispatcher: &mut Dispatcher) -> Result<u8, DispatchError> {
fn root_command() -> CommandDefinition {
let command = CommandDefinition::new("root")
.add_flag((' ', "shared", "show root of the shared repo", false))
.add_flag(RootOpts::flags()[0].clone())
.with_doc(
r#"print the root (top) of the current working directory
@ -44,25 +45,27 @@ fn root_command() -> CommandDefinition {
command
}
pub struct RootCommand {
shared: bool,
}
define_flags! {
pub struct RootOpts {
/// show root of the shared repo
shared: bool,
impl From<ParseOutput> for RootCommand {
fn from(opts: ParseOutput) -> Self {
let shared: bool = opts.pick("shared");
#[args]
args: Vec<String>,
}
RootCommand { shared }
pub struct DebugstoreOpts {
/// print blob contents
content: bool,
#[args]
args: Vec<String>,
}
}
pub fn root(
cmd: RootCommand,
args: Vec<String>,
io: &mut IO,
repo: Repo,
) -> Result<u8, DispatchError> {
if args.len() > 0 {
pub fn root(opts: RootOpts, io: &mut IO, repo: Repo) -> Result<u8, DispatchError> {
let args = opts.args;
if args != vec!["root"] {
return Err(DispatchError::InvalidArguments {
command_name: "root".to_string(),
}); // root doesn't support arguments
@ -70,7 +73,7 @@ pub fn root(
let shared = repo.sharedpath()?;
let path = if cmd.shared {
let path = if opts.shared {
shared.unwrap_or(repo.path().to_owned())
} else {
repo.path().to_owned()
@ -93,25 +96,9 @@ fn debugstore_command() -> CommandDefinition {
command
}
pub struct DebugstoreCommand {
content: bool,
}
impl From<ParseOutput> for DebugstoreCommand {
fn from(opts: ParseOutput) -> Self {
let content: bool = opts.pick("shared");
DebugstoreCommand { content }
}
}
pub fn debugstore(
cmd: DebugstoreCommand,
args: Vec<String>,
io: &mut IO,
repo: Repo,
) -> Result<u8, DispatchError> {
if args.len() != 2 || !cmd.content {
pub fn debugstore(opts: DebugstoreOpts, io: &mut IO, repo: Repo) -> Result<u8, DispatchError> {
let args = opts.args;
if args.len() != 2 || !opts.content {
return Err(DispatchError::InvalidArguments {
command_name: "debugstore".to_string(),
}); // debugstore requires arguments