use {
clap::{
self,
ArgMatches,
},
crate::{
app::App,
app_context::AppContext,
conf::Conf,
errors::{ProgramError, TreeBuildError},
external::Launchable,
shell_install::{
ShellInstall,
ShellInstallState,
},
skin,
tree_options::TreeOptions,
verb_store::VerbStore,
},
std::{
env,
io,
path::{Path, PathBuf},
},
};
struct InstallLaunchArgs {
install: bool, set_install_state: Option<ShellInstallState>, print_shell_function: Option<String>, }
impl InstallLaunchArgs {
fn from(cli_args: &ArgMatches<'_>) -> Result<Self, ProgramError> {
let install = cli_args.is_present("install");
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()?;
Ok(Self {
install,
set_install_state,
print_shell_function,
})
}
}
pub struct AppLaunchArgs {
pub root: PathBuf, pub file_export_path: Option<String>, pub cmd_export_path: Option<String>, pub tree_options: TreeOptions, pub commands: Option<String>, pub height: Option<u16>, pub no_style: bool, }
#[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() {
Err(TreeBuildError::FileNotFound {
path: format!("{:?}", &root),
})?;
}
if !root.is_dir() {
if let Some(parent) = root.parent() {
info!("Passed path isn't a directory => opening parent instead");
root = parent.to_path_buf();
} else {
Err(TreeBuildError::NotADirectory {
path: format!("{:?}", &root),
})?;
}
}
Ok(canonicalize_root(&root)?)
}
pub fn run() -> Result<Option<Launchable>, ProgramError> {
let clap_app = crate::clap::clap_app();
let cli_matches = clap_app.get_matches();
let install_args = InstallLaunchArgs::from(&cli_matches)?;
let mut must_quit = false;
if let Some(state) = install_args.set_install_state {
state.write_file()?;
must_quit = true;
}
if let Some(shell) = &install_args.print_shell_function {
ShellInstall::print(shell)?;
must_quit = true;
}
if must_quit {
return Ok(None);
}
let specific_conf: Option<Vec<PathBuf>> = cli_matches.value_of("conf")
.map(|s| s.split(';').map(PathBuf::from).collect());
if specific_conf.is_none() {
let mut shell_install = ShellInstall::new(install_args.install);
shell_install.check()?;
if shell_install.should_quit {
return Ok(None);
}
}
let config = match &specific_conf {
Some(conf_paths) => {
let mut conf = Conf::default();
for path in conf_paths {
conf.read_file(path)?;
}
conf
}
_ => {
Conf::from_default_location()?
}
};
let mut tree_options = TreeOptions::default();
if !config.default_flags.is_empty() {
debug!("Applying default flags {:?} from conf", &config.default_flags);
let clap_app = crate::clap::clap_app()
.setting(clap::AppSettings::NoBinaryName);
let flags_args = format!("-{}", &config.default_flags);
let conf_matches = clap_app.get_matches_from(vec![&flags_args]);
tree_options.apply(&conf_matches);
debug!("modified tree options: {:?}", &tree_options);
}
tree_options.apply(&cli_matches);
let mut verb_store = VerbStore::new();
verb_store.init(&config);
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 no_style = cli_matches.is_present("no-style");
let height = cli_matches.value_of("height").and_then(|s| s.parse().ok());
let root = get_root_path(&cli_matches)?;
let launch_args = AppLaunchArgs {
root,
file_export_path,
cmd_export_path,
tree_options,
commands,
height,
no_style,
};
let context = AppContext::from(launch_args, verb_store);
let skin = skin::Skin::create(config.skin);
App::new().run(crate::io::writer(), &context, skin)
}
pub fn ask_authorization() -> Result<bool, ProgramError> {
let mut answer = String::new();
io::stdin().read_line(&mut answer)?;
let answer = answer.trim();
Ok(match answer.as_ref() {
"n" | "N" => false,
_ => true,
})
}