233 feature request subcommands (#234)

This commit is contained in:
Christian Visintin
2024-03-01 10:01:25 +01:00
committed by GitHub
parent 2a51ab984c
commit 679a829744
16 changed files with 154 additions and 103 deletions

View File

@@ -34,36 +34,25 @@ Address syntax can be:
Please, report issues to <https://github.com/veeso/termscp>
Please, consider supporting the author <https://ko-fi.com/veeso>")]
pub struct Args {
#[argh(
switch,
short = 'b',
description = "resolve address argument as a bookmark name"
)]
#[argh(subcommand)]
pub nested: Option<ArgsSubcommands>,
/// resolve address argument as a bookmark name
#[argh(switch, short = 'b')]
pub address_as_bookmark: bool,
#[argh(switch, short = 'c', description = "open termscp configuration")]
pub config: bool,
#[argh(switch, short = 'D', description = "enable TRACE log level")]
/// enable TRACE log level
#[argh(switch, short = 'D')]
pub debug: bool,
#[argh(option, short = 'P', description = "provide password from CLI")]
/// provide password from CLI
#[argh(option, short = 'P')]
pub password: Option<String>,
#[argh(switch, short = 'q', description = "disable logging")]
/// disable logging
#[argh(switch, short = 'q')]
pub quiet: bool,
#[argh(option, short = 't', description = "import specified theme")]
pub theme: Option<String>,
#[argh(
switch,
short = 'u',
description = "update termscp to the latest version"
)]
pub update: bool,
#[argh(
option,
short = 'T',
default = "10",
description = "set UI ticks; default 10ms"
)]
/// set UI ticks; default 10ms
#[argh(option, short = 'T', default = "10")]
pub ticks: u64,
#[argh(switch, short = 'v', description = "print version")]
/// print version
#[argh(switch, short = 'v')]
pub version: bool,
// -- positional
#[argh(
@@ -73,6 +62,33 @@ pub struct Args {
pub positional: Vec<String>,
}
#[derive(FromArgs)]
#[argh(subcommand)]
pub enum ArgsSubcommands {
Config(ConfigArgs),
LoadTheme(LoadThemeArgs),
Update(UpdateArgs),
}
#[derive(FromArgs)]
/// open termscp configuration
#[argh(subcommand, name = "config")]
pub struct ConfigArgs {}
#[derive(FromArgs)]
/// import the specified theme
#[argh(subcommand, name = "update")]
pub struct UpdateArgs {}
#[derive(FromArgs)]
/// import the specified theme
#[argh(subcommand, name = "theme")]
pub struct LoadThemeArgs {
#[argh(positional)]
/// theme file
pub theme: PathBuf,
}
pub struct RunOpts {
pub remote: Remote,
pub ticks: Duration,
@@ -80,6 +96,29 @@ pub struct RunOpts {
pub task: Task,
}
impl RunOpts {
pub fn config() -> Self {
Self {
task: Task::Activity(NextActivity::SetupActivity),
..Default::default()
}
}
pub fn update() -> Self {
Self {
task: Task::InstallUpdate,
..Default::default()
}
}
pub fn import_theme(theme: PathBuf) -> Self {
Self {
task: Task::ImportTheme(theme),
..Default::default()
}
}
}
impl Default for RunOpts {
fn default() -> Self {
Self {

View File

@@ -3,7 +3,6 @@
//! `config` is the module which provides access to all the termscp configurations
// export
pub use params::*;
pub mod bookmarks;
pub mod params;

View File

@@ -1081,10 +1081,7 @@ mod tests {
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
let host: Localhost = Localhost::new(PathBuf::from(tmpdir.path())).ok().unwrap();
// Execute
#[cfg(unix)]
assert_eq!(host.exec("echo 5").ok().unwrap().as_str(), "5\n");
#[cfg(windows)]
assert_eq!(host.exec("echo 5").ok().unwrap().as_str(), "5\r\n");
assert!(host.exec("echo 5").ok().unwrap().as_str().contains("5"));
}
#[test]

View File

@@ -32,7 +32,7 @@ mod utils;
// namespaces
use activity_manager::{ActivityManager, NextActivity};
use cli_opts::{Args, BookmarkParams, HostParams, Remote, RunOpts, Task};
use cli_opts::{Args, ArgsSubcommands, BookmarkParams, HostParams, Remote, RunOpts, Task};
use filetransfer::FileTransferParams;
use system::logging::{self, LogLevel};
@@ -63,59 +63,57 @@ fn main() {
/// In case of success returns `RunOpts`
/// in case something is wrong returns the error message
fn parse_args(args: Args) -> Result<RunOpts, String> {
let mut run_opts: RunOpts = RunOpts::default();
// Version
if args.version {
return Err(format!(
"termscp - {TERMSCP_VERSION} - Developed by {TERMSCP_AUTHORS}",
));
}
// Setup activity?
if args.config {
run_opts.task = Task::Activity(NextActivity::SetupActivity);
}
// Logging
if args.debug {
run_opts.log_level = LogLevel::Trace;
} else if args.quiet {
run_opts.log_level = LogLevel::Off;
}
// Match ticks
run_opts.ticks = Duration::from_millis(args.ticks);
// @! extra modes
if let Some(theme) = args.theme.as_deref() {
run_opts.task = Task::ImportTheme(PathBuf::from(theme));
}
if args.update {
run_opts.task = Task::InstallUpdate;
}
// @! Ordinary mode
// Remote argument
match parse_address_arg(&args) {
Err(err) => return Err(err),
Ok(Remote::None) => {}
Ok(remote) => {
// Set params
run_opts.remote = remote;
// In this case the first activity will be FileTransfer
run_opts.task = Task::Activity(NextActivity::FileTransfer);
}
}
let run_opts = match args.nested {
Some(ArgsSubcommands::Update(_)) => RunOpts::update(),
Some(ArgsSubcommands::LoadTheme(args)) => RunOpts::import_theme(args.theme),
Some(ArgsSubcommands::Config(_)) => RunOpts::config(),
None => {
let mut run_opts: RunOpts = RunOpts::default();
// Version
if args.version {
return Err(format!(
"termscp - {TERMSCP_VERSION} - Developed by {TERMSCP_AUTHORS}",
));
}
// Logging
if args.debug {
run_opts.log_level = LogLevel::Trace;
} else if args.quiet {
run_opts.log_level = LogLevel::Off;
}
// Match ticks
run_opts.ticks = Duration::from_millis(args.ticks);
// Remote argument
match parse_address_arg(&args) {
Err(err) => return Err(err),
Ok(Remote::None) => {}
Ok(remote) => {
// Set params
run_opts.remote = remote;
// In this case the first activity will be FileTransfer
run_opts.task = Task::Activity(NextActivity::FileTransfer);
}
}
// Local directory
if let Some(localdir) = args.positional.get(1) {
// Change working directory if local dir is set
let localdir: PathBuf = PathBuf::from(localdir);
if let Err(err) = env::set_current_dir(localdir.as_path()) {
return Err(format!("Bad working directory argument: {err}"));
// Local directory
if let Some(localdir) = args.positional.get(1) {
// Change working directory if local dir is set
let localdir: PathBuf = PathBuf::from(localdir);
if let Err(err) = env::set_current_dir(localdir.as_path()) {
return Err(format!("Bad working directory argument: {err}"));
}
}
run_opts
}
}
};
Ok(run_opts)
}
/// Parse address argument from cli args
fn parse_address_arg(args: &Args) -> Result<Remote, String> {
if let Some(remote) = args.positional.get(0) {
if let Some(remote) = args.positional.first() {
if args.address_as_bookmark {
Ok(Remote::Bookmark(BookmarkParams::new(
remote,
@@ -197,5 +195,6 @@ fn run_activity(activity: NextActivity, ticks: Duration, remote: Remote) -> i32
Remote::None => {}
}
manager.run(activity);
0
}

View File

@@ -419,7 +419,7 @@ mod tests {
use tempfile::TempDir;
use super::*;
use crate::config::UserConfig;
use crate::config::params::UserConfig;
use crate::utils::random::random_alphanumeric_with_len;
#[test]

View File

@@ -48,7 +48,7 @@ impl SshKeyStorage {
.query(host)
.identity_file
.as_ref()
.and_then(|x| x.get(0).cloned());
.and_then(|x| x.first().cloned());
key
})

View File

@@ -7,9 +7,6 @@ pub mod setup;
pub mod ssh_keys;
pub mod theme;
pub use setup::*;
pub use ssh_keys::*;
pub use theme::*;
use tuirealm::event::{Key, KeyEvent, KeyModifiers};
use tuirealm::tui::widgets::Clear;
use tuirealm::{Frame, Sub, SubClause, SubEventClause};

View File

@@ -60,8 +60,8 @@ where
}
(None, _) => comps.push(Component::ParentDir),
(Some(a), Some(b)) if comps.is_empty() && a == b => (),
(Some(a), Some(b)) if b == Component::CurDir => comps.push(a),
(Some(_), Some(b)) if b == Component::ParentDir => return None,
(Some(a), Some(Component::CurDir)) => comps.push(a),
(Some(_), Some(Component::ParentDir)) => return None,
(Some(a), Some(_)) => {
comps.push(Component::ParentDir);
for _ in itb {