| |
|
| |
pub enum Operation {
|
| |
List { opts: ListOptions },
|
| + |
Other { args: Vec<OsString> },
|
| |
}
|
| |
|
| |
#[derive(PartialEq, Eq)]
|
| |
pub enum OperationName {
|
| |
List,
|
| + |
Other,
|
| |
}
|
| |
|
| |
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
| |
pub struct ListOptions {
|
| |
mode: common::Mode,
|
| |
filter: cob::issue::Filter,
|
| + |
json: bool,
|
| |
}
|
| |
|
| |
impl Args for Options {
|
| |
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
|
| |
use lexopt::prelude::*;
|
| |
|
| - |
let mut parser = lexopt::Parser::from_args(args);
|
| - |
let mut op: Option<OperationName> = None;
|
| + |
let mut parser = lexopt::Parser::from_args(args.clone());
|
| + |
let mut op = OperationName::List;
|
| |
let mut repo = None;
|
| + |
let mut forward = None;
|
| + |
let mut json = false;
|
| + |
let mut help = false;
|
| |
let mut list_opts = ListOptions::default();
|
| |
|
| |
while let Some(arg) = parser.next()? {
|
| |
match arg {
|
| + |
Long("no-forward") => {
|
| + |
forward = Some(false);
|
| + |
}
|
| + |
Long("json") => {
|
| + |
json = true;
|
| + |
}
|
| |
Long("help") | Short('h') => {
|
| - |
return Err(Error::Help.into());
|
| + |
help = true;
|
| + |
// Only enable forwarding if it was not already disabled explicitly
|
| + |
forward = match forward {
|
| + |
Some(false) => Some(false),
|
| + |
_ => Some(true),
|
| + |
};
|
| |
}
|
| |
|
| |
// select options.
|
| - |
Long("mode") | Short('m') if op == Some(OperationName::List) => {
|
| + |
Long("mode") | Short('m') if op == OperationName::List => {
|
| |
let val = parser.value()?;
|
| |
let val = val.to_str().unwrap_or_default();
|
| |
|
| |
repo = Some(rid);
|
| |
}
|
| |
|
| - |
Value(val) if op.is_none() => match val.to_string_lossy().as_ref() {
|
| - |
"list" => op = Some(OperationName::List),
|
| - |
unknown => anyhow::bail!("unknown operation '{}'", unknown),
|
| + |
Value(val) if op == OperationName::List => match val.to_string_lossy().as_ref() {
|
| + |
"list" => op = OperationName::List,
|
| + |
_ => op = OperationName::Other,
|
| |
},
|
| - |
_ => return Err(anyhow!(arg.unexpected())),
|
| + |
_ => {
|
| + |
if op == OperationName::List {
|
| + |
return Err(anyhow!(arg.unexpected()));
|
| + |
}
|
| + |
}
|
| |
}
|
| |
}
|
| |
|
| - |
let op = match op.ok_or_else(|| anyhow!("an operation must be provided"))? {
|
| - |
OperationName::List => Operation::List { opts: list_opts },
|
| + |
// Disable forwarding if it was not enabled via `--help` or was
|
| + |
// not disabled explicitly.
|
| + |
let forward = forward.unwrap_or_default();
|
| + |
|
| + |
// Show local help
|
| + |
if help && !forward {
|
| + |
return Err(Error::Help.into());
|
| + |
}
|
| + |
|
| + |
// Map local commands. Forward help and ignore `no-forward`.
|
| + |
let op = match op {
|
| + |
OperationName::List if !forward => Operation::List {
|
| + |
opts: ListOptions { json, ..list_opts },
|
| + |
},
|
| + |
_ => Operation::Other { args },
|
| |
};
|
| + |
|
| |
Ok((Options { op, repo }, vec![]))
|
| |
}
|
| |
}
|
| |
filter: opts.filter.clone(),
|
| |
};
|
| |
|
| - |
let output = list::App::new(context, terminal_info).run().await?;
|
| + |
let selection = list::App::new(context, terminal_info).run().await?;
|
| + |
|
| + |
if opts.json {
|
| + |
let selection = selection
|
| + |
.map(|o| serde_json::to_string(&o).unwrap_or_default())
|
| + |
.unwrap_or_default();
|
| |
|
| - |
let output = output
|
| - |
.map(|o| serde_json::to_string(&o).unwrap_or_default())
|
| - |
.unwrap_or_default();
|
| + |
log::info!("About to print to `stderr`: {}", selection);
|
| + |
log::info!("Exiting issue listing interface..");
|
| |
|
| - |
log::info!("About to print to `stderr`: {}", output);
|
| - |
log::info!("Exiting issue listing interface..");
|
| + |
eprint!("{selection}");
|
| + |
} else if let Some(selection) = selection {
|
| + |
let mut args = vec![];
|
| |
|
| - |
eprint!("{output}");
|
| + |
if let Some(operation) = selection.operation {
|
| + |
args.push(operation.to_string());
|
| + |
}
|
| + |
if let Some(id) = selection.ids.first() {
|
| + |
args.push(format!("{id}"));
|
| + |
}
|
| + |
|
| + |
let args = args.into_iter().map(OsString::from).collect::<Vec<_>>();
|
| + |
let _ = crate::terminal::run_rad("issue", &args);
|
| + |
}
|
| + |
}
|
| + |
Operation::Other { args } => {
|
| + |
let _ = crate::terminal::run_rad("issue", &args);
|
| |
}
|
| |
}
|
| |
|
| |
Ok(())
|
| |
}
|
| + |
|
| + |
#[cfg(test)]
|
| + |
mod cli {
|
| + |
use std::process::Command;
|
| + |
|
| + |
use assert_cmd::prelude::*;
|
| + |
|
| + |
use predicates::prelude::*;
|
| + |
|
| + |
mod assert {
|
| + |
use predicates::prelude::*;
|
| + |
use predicates::str::ContainsPredicate;
|
| + |
|
| + |
pub fn is_tui() -> ContainsPredicate {
|
| + |
predicate::str::contains("Inappropriate ioctl for device")
|
| + |
}
|
| + |
|
| + |
pub fn is_rad_manual() -> ContainsPredicate {
|
| + |
predicate::str::contains("rad-issue")
|
| + |
}
|
| + |
|
| + |
pub fn is_issue_help() -> ContainsPredicate {
|
| + |
predicate::str::contains("Terminal interfaces for issues")
|
| + |
}
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn empty_operation() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.arg("issue");
|
| + |
cmd.assert().failure().stdout(assert::is_tui());
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn empty_operation_is_not_forwarded() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.arg("issue");
|
| + |
cmd.assert().failure().stdout(assert::is_tui());
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn empty_operation_with_help_is_forwarded() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.args(["issue", "--help"]);
|
| + |
cmd.assert().success().stdout(assert::is_rad_manual());
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn empty_operation_with_help_is_not_forwarded() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.args(["issue", "--help", "--no-forward"]);
|
| + |
cmd.assert().success().stdout(assert::is_issue_help());
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn empty_operation_is_not_forwarded_explicitly() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.args(["issue", "--no-forward"]);
|
| + |
cmd.assert().failure().stdout(assert::is_tui());
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn list_operation_is_not_forwarded() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.args(["issue", "list"]);
|
| + |
cmd.assert().failure().stdout(assert::is_tui());
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn list_operation_is_not_forwarded_explicitly() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.args(["issue", "list", "--no-forward"]);
|
| + |
cmd.assert().failure().stdout(assert::is_tui());
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn list_operation_with_help_is_forwarded() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.args(["issue", "list", "--help"]);
|
| + |
cmd.assert().success().stdout(assert::is_rad_manual());
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn list_operation_with_help_is_not_forwarded() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.args(["issue", "list", "--help", "--no-forward"]);
|
| + |
cmd.assert().success().stdout(assert::is_issue_help());
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn list_operation_with_help_is_not_forwarded_reversed() -> Result<(), Box<dyn std::error::Error>>
|
| + |
{
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.args(["issue", "list", "--no-forward", "--help"]);
|
| + |
cmd.assert().success().stdout(assert::is_issue_help());
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn unknown_operation_show_is_forwarded() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.args(["issue", "show"]);
|
| + |
cmd.assert().success().stdout(predicate::str::contains(
|
| + |
"Error: rad issue: an issue must be provided",
|
| + |
));
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn unknown_operation_edit_is_forwarded() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.args(["issue", "edit"]);
|
| + |
cmd.assert().success().stdout(predicate::str::contains(
|
| + |
"Error: rad issue: an issue must be provided",
|
| + |
));
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
|
| + |
#[test]
|
| + |
#[ignore = "breaks stdout"]
|
| + |
fn unknown_operation_is_not_forwarded() -> Result<(), Box<dyn std::error::Error>> {
|
| + |
let mut cmd = Command::cargo_bin("rad-tui")?;
|
| + |
|
| + |
cmd.args(["issue", "operation", "--no-forward"]);
|
| + |
cmd.assert().success().stdout(predicate::str::contains(
|
| + |
"Error: rad issue: unknown operation",
|
| + |
));
|
| + |
|
| + |
Ok(())
|
| + |
}
|
| + |
}
|