summaryrefslogtreecommitdiffstats
path: root/src/cli
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli')
-rw-r--r--src/cli/app_launch_args.rs22
-rw-r--r--src/cli/args.rs200
-rw-r--r--src/cli/clap_args.rs235
-rw-r--r--src/cli/install_launch_args.rs19
-rw-r--r--src/cli/mod.rs110
5 files changed, 225 insertions, 361 deletions
diff --git a/src/cli/app_launch_args.rs b/src/cli/app_launch_args.rs
deleted file mode 100644
index 03b25ae..0000000
--- a/src/cli/app_launch_args.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use {
- crate::{
- tree::TreeOptions,
- },
- std::{
- path::PathBuf,
- },
-};
-
-
-/// the parsed program launch arguments which are kept for the
-/// life of the program
-pub struct AppLaunchArgs {
- pub root: PathBuf, // what should be the initial root
- pub file_export_path: Option<String>, // where to write the produced path (if required with --out) - deprecated
- pub cmd_export_path: Option<String>, // where to write the produced command (if required with --outcmd)
- pub tree_options: TreeOptions, // initial tree options
- pub commands: Option<String>, // commands passed as cli argument, still unparsed
- pub height: Option<u16>, // an optional height to replace the screen's one
- pub color: Option<bool>, // whether to display colors and styles
- pub listen: Option<String>, // if some, broot will start in serve mode on this socket
-}
diff --git a/src/cli/args.rs b/src/cli/args.rs
new file mode 100644
index 0000000..9ad810e
--- /dev/null
+++ b/src/cli/args.rs
@@ -0,0 +1,200 @@
+use {
+ std::{
+ path::PathBuf,
+ str::FromStr,
+ },
+};
+
+#[derive(Debug, clap::Parser)]
+/// A tree explorer and a customizable launcher
+///
+/// Complete documentation lives at https://dystroy.org/broot"
+#[clap(author, version, about)]
+pub struct Args {
+
+ /// Show the last modified date of files and directories"
+ #[clap(short, long, action)]
+ pub dates: bool,
+
+ /// Don't show the last modified date"
+ #[clap(short='D', long, action)]
+ pub no_dates: bool,
+
+ #[clap(short='f', long, action)]
+ /// Only show folders
+ pub only_folders: bool,
+
+ /// Show folders and files alike
+ #[clap(short='F', long, action)]
+ pub no_only_folders: bool,
+
+ /// Show filesystem info on top
+ #[clap(long, action)]
+ pub show_root_fs: bool,
+
+ /// Show git statuses on files and stats on repo
+ #[clap(short='g', long, action)]
+ pub show_git_info: bool,
+
+ /// Don't show git statuses on files and stats on repo
+ #[clap(short='G', long, action)]
+ pub no_show_git_info: bool,
+
+ #[clap(long, action)]
+ /// Only show files having an interesting git status, including hidden ones
+ pub git_status: bool,
+
+ #[clap(short='h', long, action)]
+ /// Show hidden files
+ pub hidden: bool,
+
+ #[clap(short='H', long, action)]
+ /// Don't show hidden files
+ pub no_hidden: bool,
+
+ #[clap(short='i', long, action)]
+ /// Show git ignored files
+ pub git_ignored: bool,
+
+ #[clap(short='I', long, action)]
+ /// Don't show git ignored files
+ pub no_git_ignored: bool,
+
+ #[clap(short='p', long, action)]
+ /// Show permissions
+ pub permissions: bool,
+
+ #[clap(short='P', long, action)]
+ /// Don't show permissions
+ pub no_permissions: bool,
+
+ #[clap(short='s', long, action)]
+ /// Show the size of files and directories
+ pub sizes: bool,
+
+ #[clap(short='S', long, action)]
+ /// Don't show sizes
+ pub no_sizes: bool,
+
+ #[clap(long, action)]
+ /// Sort by count (only show one level of the tree)
+ pub sort_by_count: bool,
+
+ #[clap(long, action)]
+ /// Sort by date (only show one level of the tree)
+ pub sort_by_date: bool,
+
+ #[clap(long, action)]
+ /// Sort by size (only show one level of the tree)
+ pub sort_by_size: bool,
+
+ /// Sort by size, show ignored and hidden files
+ #[clap(short, long, action)]
+ pub whale_spotting: bool,
+
+ /// Don't sort
+ #[clap(long, action)]
+ pub no_sort: bool,
+
+ /// Trim the root too and don't show a scrollbar
+ #[clap(short='t', long, action)]
+ pub trim_root: bool,
+
+ /// Don't trim the root level, show a scrollbar
+ #[clap(short='T', long, action)]
+ pub no_trim_root: bool,
+
+ /// Where to write the produced cmd (if any)
+ #[clap(long, value_parser)]
+ pub cmd_export_path: Option<PathBuf>,
+
+ /// Semicolon separated commands to execute
+ #[clap(short, long, value_parser)]
+ pub commands: Option<String>,
+
+ /// Whether to have styles and colors (auto is default and usually OK)
+ #[clap(long, arg_enum, value_parser, default_value="auto")]
+ pub color: TriBool,
+
+ /// Semicolon separated paths to specific config files"),
+ #[clap(long, value_parser)]
+ pub conf: Option<String>,
+
+ /// Height (if you don't want to fill the screen or for file export)
+ #[clap(long, value_parser)]
+ pub height: Option<u16>,
+
+ /// Install or reinstall the br shell function
+ #[clap(long, action)]
+ pub install: bool,
+
+ /// Where to write the produced cmd (if any)
+ #[clap(long, value_parser)]
+ pub set_install_state: Option<ShellInstallState>,
+
+ /// Print to stdout the br function for a given shell
+ #[clap(long, value_parser)]
+ pub print_shell_function: Option<String>,
+
+ /// A socket to listen to for commands
+ #[cfg(unix)]
+ #[clap(long, value_parser)]
+ pub listen: Option<String>,
+
+ /// Ask for the current root of the remote broot
+ #[cfg(unix)]
+ #[clap(long, action)]
+ pub get_root: bool,
+
+ /// A socket that broot sends commands to before quitting
+ #[cfg(unix)]
+ #[clap(long, value_parser)]
+ pub send: Option<String>,
+
+ /// Root Directory
+ #[clap(value_parser, value_name="FILE")]
+ pub root: Option<PathBuf>,
+}
+
+/// This is an Option<bool> but I didn't find any way to configure
+/// clap to parse an Option<T> as I want
+#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ArgEnum)]
+pub enum TriBool {
+ Auto,
+ Yes,
+ No,
+}
+impl TriBool {
+ pub fn unwrap_or_else<F>(self, f: F) -> bool
+ where
+ F: FnOnce() -> bool
+ {
+ match self {
+ Self::Auto => f(),
+ Self::Yes => true,
+ Self::No => false,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, clap::ValueEnum)]
+pub enum ShellInstallState {
+ Undefined, // before any install, this is the initial state
+ Refused,
+ Installed,
+}
+impl FromStr for ShellInstallState {
+ type Err = String;
+ fn from_str(state: &str) -> Result<Self, Self::Err> {
+ match state {
+ "undefined" => Ok(Self::Undefined),
+ "refused" => Ok(Self::Refused),
+ "installed" => Ok(Self::Installed),
+ _ => Err(
+ // not supposed to happen because claps check the values
+ format!("unexpected install state: {:?}", state)
+ ),
+ }
+ }
+}
+
diff --git a/src/cli/clap_args.rs b/src/cli/clap_args.rs
deleted file mode 100644
index 0ac9524..0000000
--- a/src/cli/clap_args.rs
+++ /dev/null
@@ -1,235 +0,0 @@
-/// this module generate the clap App, which defines
-/// launch arguments
-
-/// Declare the possible CLI arguments
-pub fn clap_app() -> clap::Command<'static> {
- let app = clap::Command::new("broot")
- .version(env!("CARGO_PKG_VERSION"))
- .author("dystroy <denys.seguret@gmail.com>")
- .about(
- "A tree explorer and a customizable launcher\n\
- Complete documentation lives at https://dystroy.org/broot"
- )
- .arg(clap::Arg::new("ROOT").help("sets the root directory"))
- // tree flags
- .arg(
- clap::Arg::new("dates")
- .short('d')
- .long("dates")
- .help("Show the last modified date of files and directories"),
- )
- .arg(
- clap::Arg::new("no-dates")
- .short('D')
- .long("no-dates")
- .help("Don't show last modified date"),
- )
- .arg(
- clap::Arg::new("only-folders")
- .short('f')
- .long("only-folders")
- .help("Only show folders"),
- )
- .arg(
- clap::Arg::new("no-only-folders")
- .short('F')
- .long("no-only-folders")
- .help("Show folders and files alike"),
- )
- .arg(
- clap::Arg::new("show-root-fs")
- .long("show-root-fs")
- .help("Show filesystem info on top"),
- )
- .arg(
- clap::Arg::new("show-git-info")
- .short('g')
- .long("show-git-info")
- .help("Show git statuses on files and stats on repo"),
- )
- .arg(
- clap::Arg::new("no-show-git-info")
- .short('G')
- .long("no-show-git-info")
- .help("Don't show git statuses on files"),
- )
- .arg(
- clap::Arg::new("git-status")
- .long("git-status")
- .help("Only show files having an interesting git status, including hidden ones"),
- )
- .arg(
- clap::Arg::new("hidden")
- .short('h')
- .long("hidden")
- .help("Show hidden files"),
- )
- .arg(
- clap::Arg::new("no-hidden")
- .short('H')
- .long("no-hidden")
- .help("Don't show hidden files"),
- )
- .arg(
- clap::Arg::new("show-gitignored")
- .short('i')
- .long("show-gitignored")
- .help("Show files which should be ignored according to git"),
- )
- .arg(
- clap::Arg::new("no-show-gitignored")
- .short('I')
- .long("no-show-gitignored")
- .help("Don't show gitignored files"),
- )
- .arg(
- clap::Arg::new("permissions")
- .short('p')
- .long("permissions")
- .help("Show permissions, with owner and group"),
- )
- .arg(
- clap::Arg::new("no-permissions")
- .short('P')
- .long("no-permissions")
- .help("Don't show permissions"),
- )
- .arg(
- clap::Arg::new("sizes")
- .short('s')
- .long("sizes")
- .help("Show the size of files and directories"),
- )
- .arg(
- clap::Arg::new("no-sizes")
- .short('S')
- .long("no-sizes")
- .help("Don't show sizes"),
- )
- .arg(
- clap::Arg::new("sort-by-count")
- .long("sort-by-count")
- .help("Sort by count (only show one level of the tree)"),
- )
- .arg(
- clap::Arg::new("sort-by-date")
- .long("sort-by-date")
- .help("Sort by date (only show one level of the tree)"),
- )
- .arg(
- clap::Arg::new("sort-by-size")
- .long("sort-by-size")
- .help("Sort by size (only show one level of the tree)"),
- )
- .arg(
- clap::Arg::new("whale-spotting")
- .short('w')
- .long("whale-spotting")
- .help("Sort by size, show ignored and hidden files"),
- )
- .arg(
- clap::Arg::new("no-sort")
- .long("no-sort")
- .help("Don't sort"),
- )
- .arg(
- clap::Arg::new("trim-root")
- .short('t')
- .long("trim-root")
- .help("Trim the root too and don't show a scrollbar"),
- )
- .arg(
- clap::Arg::new("no-trim-root")
- .short('T')
- .long("no-trim-root")
- .help("Don't trim the root level, show a scrollbar"),
- )
- // other options
- .arg(
- clap::Arg::new("cmd-export-path")
- .long("outcmd")
- .takes_value(true)
- .help("Where to write the produced cmd (if any)"),
- )
- .arg(
- clap::Arg::new("commands")
- .short('c')
- .long("cmd")
- .takes_value(true)
- .help("Semicolon separated commands to execute"),
- )
- .arg(
- clap::Arg::new("color")
- .long("color")
- .takes_value(true)
- .possible_values(&["yes", "no", "auto"])
- .default_value("auto")
- .help("Whether to have styles and colors (auto is default and usually OK)"),
- )
- .arg(
- clap::Arg::new("conf")
- .long("conf")
- .takes_value(true)
- .help("Semicolon separated paths to specific config files"),
- )
- .arg(
- clap::Arg::new("height")
- .long("height")
- .help("Height (if you don't want to fill the screen or for file export)")
- .takes_value(true),
- )
- .arg(
- clap::Arg::new("file-export-path") // deprecated since broot 1.6
- .short('o')
- .long("out")
- .takes_value(true)
- .hide(true)
- .help("Where to write the produced path (if any)"),
- )
- .arg(
- clap::Arg::new("install")
- .long("install")
- .help("Install or reinstall the br shell function"),
- )
- .arg(
- clap::Arg::new("set-install-state")
- .long("set-install-state")
- .takes_value(true)
- .value_name("state")
- .possible_values(&["undefined", "refused", "installed"])
- .help("Set the installation state (for use in install script)"),
- )
- .arg(
- clap::Arg::new("print-shell-function")
- .long("print-shell-function")
- .takes_value(true)
- .value_name("shell")
- .help("Print to stdout the br function for a given shell"),
- )
- .setting(clap::AppSettings::DeriveDisplayOrder);
- #[cfg(unix)]
- let app = app
- .arg(
- clap::Arg::new("listen")
- .long("listen")
- .takes_value(true)
- .help("Listen for commands")
- )
- .arg(
- clap::Arg::new("get-root")
- .long("get-root")
- .help("Ask for the current root of the remote broot")
- )
- .arg(
- clap::Arg::new("send")
- .long("send")
- .takes_value(true)
- .help("send commands to a remote broot then quits")
- );
- app
-}
-
-#[test]
-fn verify_app() {
- clap_app().debug_assert();
-}
diff --git a/src/cli/install_launch_args.rs b/src/cli/install_launch_args.rs
index c688e12..e5b1d01 100644
--- a/src/cli/install_launch_args.rs
+++ b/src/cli/install_launch_args.rs
@@ -3,12 +3,10 @@
use {
crate::{
errors::ProgramError,
- shell_install::ShellInstallState,
+ cli::{Args, ShellInstallState},
},
- clap::{self, ArgMatches},
std::{
env,
- str::FromStr,
},
};
@@ -21,7 +19,7 @@ pub struct InstallLaunchArgs {
pub print_shell_function: Option<String>, // shell function to print on stdout
}
impl InstallLaunchArgs {
- pub fn from(cli_args: &ArgMatches) -> Result<Self, ProgramError> {
+ pub fn from(args: &Args) -> Result<Self, ProgramError> {
let mut install = None;
if let Ok(s) = env::var("BR_INSTALL") {
if s == "yes" {
@@ -33,18 +31,13 @@ impl InstallLaunchArgs {
}
}
// the cli arguments may override the env var value
- if cli_args.is_present("install") {
+ if args.install {
install = Some(true);
- } else if cli_args.value_of("cmd-export-path").is_some() {
+ } else if args.cmd_export_path.is_some() {
install = Some(false);
}
- let print_shell_function = cli_args
- .value_of("print-shell-function")
- .map(str::to_string);
- let set_install_state = cli_args
- .value_of("set-install-state")
- .map(ShellInstallState::from_str)
- .transpose()?;
+ let print_shell_function = args.print_shell_function.clone();
+ let set_install_state = args.set_install_state;
Ok(Self {
install,
set_install_state,
diff --git a/src/cli/mod.rs b/src/cli/mod.rs
index 29c5152..310a7ed 100644
--- a/src/cli/mod.rs
+++ b/src/cli/mod.rs
@@ -1,12 +1,13 @@
//! this module manages reading and translating
//! the arguments passed on launch of the application.
-pub mod clap_args;
-mod app_launch_args;
+//mod app_launch_args;
+mod args;
mod install_launch_args;
pub use {
- app_launch_args::*,
+ //app_launch_args::*,
+ args::*,
install_launch_args::*,
};
@@ -15,13 +16,12 @@ use {
app::{App, AppContext},
conf::Conf,
display,
- errors::{ProgramError, TreeBuildError},
+ errors::ProgramError,
launchable::Launchable,
- shell_install::ShellInstall,
- tree::TreeOptions,
+ shell_install::{ShellInstall, write_state},
verb::VerbStore,
},
- clap::{self, ArgMatches},
+ clap::Parser,
crossterm::{
self,
cursor,
@@ -30,65 +30,25 @@ use {
QueueableCommand,
},
std::{
- env,
io::{self, Write},
- path::{Path, PathBuf},
+ path::PathBuf,
},
};
-#[cfg(not(windows))]
-fn canonicalize_root(root: &Path) -> io::Result<PathBuf> {
- root.canonicalize()
-}
-
-#[cfg(windows)]
-fn canonicalize_root(root: &Path) -> io::Result<PathBuf> {
- Ok(if root.is_relative() {
- env::current_dir()?.join(root)
- } else {
- root.to_path_buf()
- })
-}
-
-fn get_root_path(cli_args: &ArgMatches) -> Result<PathBuf, ProgramError> {
- let mut root = cli_args
- .value_of("ROOT")
- .map_or(env::current_dir()?, PathBuf::from);
- if !root.exists() {
- return Err(TreeBuildError::FileNotFound {
- path: format!("{:?}", &root),
- }.into());
- }
- if !root.is_dir() {
- // we try to open the parent directory if the passed file isn't one
- if let Some(parent) = root.parent() {
- info!("Passed path isn't a directory => opening parent instead");
- root = parent.to_path_buf();
- } else {
- // let's give up
- return Err(TreeBuildError::NotADirectory {
- path: format!("{:?}", &root),
- }.into());
- }
- }
- Ok(canonicalize_root(&root)?)
-}
-
/// run the application, and maybe return a launchable
/// which must be run after broot
pub fn run() -> Result<Option<Launchable>, ProgramError> {
- let clap_app = clap_args::clap_app();
// parse the launch arguments we got from cli
- let cli_matches = clap_app.get_matches();
+ let args = Args::parse();
// read the install related arguments
- let install_args = InstallLaunchArgs::from(&cli_matches)?;
+ let install_args = InstallLaunchArgs::from(&args)?;
// execute installation things required by launch args
let mut must_quit = false;
if let Some(state) = install_args.set_install_state {
- state.write_file()?;
+ write_state(state)?;
must_quit = true;
}
if let Some(shell) = &install_args.print_shell_function {
@@ -100,8 +60,8 @@ pub fn run() -> Result<Option<Launchable>, ProgramError> {
}
// read the list of specific config files
- let specific_conf: Option<Vec<PathBuf>> = cli_matches
- .value_of("conf")
+ let specific_conf: Option<Vec<PathBuf>> = args.conf
+ .as_ref()
.map(|s| s.split(';').map(PathBuf::from).collect());
// if we don't run on a specific config file, we check the
@@ -128,64 +88,32 @@ pub fn run() -> Result<Option<Launchable>, ProgramError> {
};
debug!("config: {:#?}", &config);
- // tree options are built from the default_flags
- // found in the config file(s) (if any) then overriden
- // by the cli args
- let mut tree_options = TreeOptions::default();
- tree_options.apply_config(&config)?;
- tree_options.apply_launch_args(&cli_matches);
// verb store is completed from the config file(s)
let verb_store = VerbStore::new(&mut config)?;
- // reading the other arguments
- let file_export_path = cli_matches.value_of("file-export-path").map(str::to_string);
- let cmd_export_path = cli_matches.value_of("cmd-export-path").map(str::to_string);
- let commands = cli_matches.value_of("commands").map(str::to_string);
- let height = cli_matches.value_of("height").and_then(|s| s.parse().ok());
- let color = match cli_matches.value_of("color") {
- Some("yes") => Some(true),
- Some("no") => Some(false),
- _ => None,
- };
-
- let root = get_root_path(&cli_matches)?;
+ let context = AppContext::from(args, verb_store, &config)?;
#[cfg(unix)]
- if let Some(server_name) = cli_matches.value_of("send") {
+ if let Some(server_name) = &context.launch_args.send {
use crate::{
command::Sequence,
net::{Client, Message},
};
let client = Client::new(server_name);
- if let Some(seq) = &commands {
+ if let Some(seq) = &context.launch_args.commands {
let message = Message::Sequence(Sequence::new_local(seq.to_string()));
client.send(&message)?;
- } else if !cli_matches.is_present("get-root") {
- let message = Message::Command(format!(":focus {}", root.to_string_lossy()));
+ } else if !context.launch_args.get_root {
+ let message = Message::Command(format!(":focus {}", context.initial_root.to_string_lossy()));
client.send(&message)?;
};
- if cli_matches.is_present("get-root") {
+ if context.launch_args.get_root {
client.send(&Message::GetRoot)?;
}
return Ok(None);
}
- let mut launch_args = AppLaunchArgs {
- root,
- file_export_path,
- cmd_export_path,
- tree_options,
- commands,
- height,
- color,
- listen: cli_matches.value_of("listen").map(str::to_string),
- };
- if color == Some(false) {
- launch_args.tree_options.show_selection_mark = true;
- }
-
- let context = AppContext::from(launch_args, verb_store, &config)?;
let mut w = display::writer();
let app = App::new(&context)?;
w.queue(EnterAlternateScreen)?;