Skip to content

Commit

Permalink
feat(Interact): Allow setting and resetting of prefix
Browse files Browse the repository at this point in the history
  • Loading branch information
nokome committed Apr 5, 2021
1 parent 71f4cd3 commit a684126
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 27 deletions.
53 changes: 44 additions & 9 deletions rust/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,32 @@ use strum::{Display, EnumVariantNames};
setting = structopt::clap::AppSettings::VersionlessSubcommands
)]
pub struct Args {
#[structopt(subcommand)]
pub command: Option<Command>,

/// Show debug level log events (and above)
#[structopt(long, conflicts_with_all = &["info", "warn", "error"])]
#[structopt(long, global = true, conflicts_with_all = &["info", "warn", "error"])]
pub debug: bool,

/// Show info level log events (and above; default)
#[structopt(long, conflicts_with_all = &["debug", "warn", "error"])]
#[structopt(long, global = true, conflicts_with_all = &["debug", "warn", "error"])]
pub info: bool,

/// Show warning level log events (and above)
#[structopt(long, conflicts_with_all = &["debug", "info", "error"])]
#[structopt(long, global = true, conflicts_with_all = &["debug", "info", "error"])]
pub warn: bool,

/// Show error level log entries only
#[structopt(long, conflicts_with_all = &["debug", "info", "warn"])]
#[structopt(long, global = true, conflicts_with_all = &["debug", "info", "warn"])]
pub error: bool,

#[structopt(subcommand)]
pub command: Option<Command>,
/// Enter interactive mode (with any command and options as the prefix)
#[structopt(short, long, global = true)]
pub interact: bool,
}

const GLOBAL_ARGS: [&str; 6] = ["--debug", "--info", "--warn", "--error", "--interact", "-i"];

#[derive(Debug, Display, StructOpt, EnumVariantNames)]
#[strum(serialize_all = "lowercase")]
#[structopt(
Expand All @@ -48,13 +54,35 @@ pub enum Command {

pub async fn cli(args: Vec<String>) -> Result<i32> {
// Parse args into a command
let parsed_args = Args::from_iter_safe(args.clone());
let Args {
command,
debug,
info,
warn,
error,
} = Args::from_iter(args);
interact,
} = match parsed_args {
Ok(args) => args,
Err(error) => {
if args.contains(&"-i".to_string()) || args.contains(&"--interact".to_string()) {
// Parse the global options ourselves so that user can
// pass an incomplete command prefix to interactive mode
Args {
command: None,
debug: args.contains(&"--debug".to_string()),
info: args.contains(&"--info".to_string()),
warn: args.contains(&"--warn".to_string()),
error: args.contains(&"--error".to_string()),
interact: true,
}
} else {
// Print the error `clap` help or usage message and exit
println!("{}", error);
return Ok(exitcode::SOFTWARE);
}
}
};

// Determine the log level to use on stderr
let level = if debug {
Expand Down Expand Up @@ -90,8 +118,15 @@ pub async fn cli(args: Vec<String>) -> Result<i32> {
// Load plugins
plugins::read_plugins()?;

let result = if command.is_none() {
interact::run(&config).await
let result = if command.is_none() || interact {
let prefix: Vec<String> = args
.into_iter()
// Remove executable name
.skip(1)
// Remove the global args which can not be applied to each interactive line
.filter(|arg| !GLOBAL_ARGS.contains(&arg.as_str()))
.collect();
interact::run(&prefix, &config).await
} else {
match command.unwrap() {
Command::Open(args) => open::cli::run(args).await,
Expand Down
47 changes: 29 additions & 18 deletions rust/src/interact.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::cli::Command;
use crate::{config, convert, open, plugins, serve, upgrade, util::dirs};
use anyhow::{bail, Result};
use structopt::StructOpt;
Expand All @@ -14,23 +15,10 @@ pub struct Line {
pub command: Command,
}

#[derive(Debug, StructOpt)]
#[structopt(
setting = structopt::clap::AppSettings::DeriveDisplayOrder
)]
pub enum Command {
Open(open::cli::Args),
Convert(convert::cli::Args),
Serve(serve::cli::Args),
Plugins(plugins::cli::Args),
Config(config::cli::Args),
Upgrade(upgrade::cli::Args),
}

/// Run the interactive REPL
#[cfg(feature = "interact")]
#[tracing::instrument]
pub async fn run(config: &config::Config) -> Result<()> {
pub async fn run(prefix: &Vec<String>, config: &config::Config) -> Result<()> {
use rustyline::{error::ReadlineError, Editor};

let history_file = dirs::config(true)?.join("history.txt");
Expand All @@ -40,16 +28,39 @@ pub async fn run(config: &config::Config) -> Result<()> {
tracing::debug!("No previous history found")
}

let mut prefix = prefix.clone();
let mut config = config.clone();

loop {
let readline = rl.readline("> ");
match readline {
Ok(line) => {
rl.add_history_entry(line.as_str());
let clap = Line::clap();
let line = line.split_whitespace().collect::<Vec<&str>>();
match clap.get_matches_from_safe(line) {
rl.add_history_entry(&line);

let mut args = line
.split_whitespace()
.map(str::to_string)
.collect::<Vec<String>>();

if let Some(first) = line.trim_start().chars().nth(0) {
if first == '~' {
println!("Command prefix is {:?}", prefix);
continue;
} else if first == '<' {
prefix = args[1..].into();
println!("Set command prefix to {:?}", prefix);
continue;
} else if first == '>' {
prefix.clear();
println!("Cleared command prefix");
continue;
} else if first == '?' {
args[0] = "help".into();
}
};

let args = [prefix.as_slice(), args.as_slice()].concat();
match Line::clap().get_matches_from_safe(args) {
Ok(matches) => {
let Line { command } = Line::from_clap(&matches);
if let Err(error) = match command {
Expand Down

0 comments on commit a684126

Please sign in to comment.