mod args;
mod install_launch_args;
pub use {
args::*,
install_launch_args::*,
};
use {
crate::{
app::{
App,
AppContext,
},
conf::{
Conf,
write_default_conf_in,
},
display,
errors::ProgramError,
launchable::Launchable,
shell_install::{
ShellInstall,
ShellInstallState,
},
verb::VerbStore,
},
clap::{
CommandFactory,
Parser,
},
clap_help::Printer,
crokey::crossterm::{
QueueableCommand,
cursor,
event::{
DisableMouseCapture,
EnableMouseCapture,
},
terminal::{
EnterAlternateScreen,
LeaveAlternateScreen,
},
},
std::{
io::{
self,
Write,
},
path::PathBuf,
},
};
static INTRO: &str = "
**broot** lets you explore file hierarchies with a tree-like view, manipulate and preview files, launch actions, and define your own shortcuts.
**broot** is best launched as `br`: this shell function gives you access to more commands, especially `cd.` The br shell function is interactively installed on first broot launch.
Flags and options can be classically passed on launch but also written in the configuration file. Each flag has a counter-flag so that you can cancel at command line a flag which has been set in the configuration file.
Complete documentation and tips at https://dystroy.org/broot
";
pub fn run() -> Result<Option<Launchable>, ProgramError> {
let args = Args::parse();
let mut must_quit = false;
if args.help {
Printer::new(Args::command())
.with_max_width(110)
.with("introduction", INTRO)
.with("options", clap_help::TEMPLATE_OPTIONS_MERGED_VALUE)
.without("author")
.print_help();
must_quit = true;
}
if args.version {
println!("broot {}", env!("CARGO_PKG_VERSION"));
must_quit = true;
}
if let Some(dir) = &args.write_default_conf {
write_default_conf_in(dir)?;
must_quit = true;
}
let install_args = InstallLaunchArgs::from(&args)?;
let mut shell_install = ShellInstall::new(install_args.install == Some(true));
if let Some(state) = install_args.set_install_state {
let state: ShellInstallState = state.into();
state.write(&shell_install)?;
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>> = args
.conf
.as_ref()
.map(|s| s.split(';').map(PathBuf::from).collect());
if specific_conf.is_none() && install_args.install != Some(false) {
let res = shell_install.check();
if let Err(e) = &res {
shell_install.comment_error(e);
}
res?;
if shell_install.should_quit {
return Ok(None);
}
}
let mut config = match &specific_conf {
Some(conf_paths) => {
let mut conf = Conf::default();
for path in conf_paths {
conf.read_file(path.clone())?;
}
conf
}
_ => time!(Conf::from_default_location())?,
};
debug!("config: {:#?}", &config);
let verb_store = VerbStore::new(&mut config)?;
let mut context = AppContext::from(args, verb_store, &config)?;
#[cfg(unix)]
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) = &context.launch_args.cmd {
let message = Message::Sequence(Sequence::new_local(seq.clone()));
client.send(&message)?;
} else if !context.launch_args.get_root {
let message =
Message::Command(format!(":focus {}", context.initial_root.to_string_lossy()));
client.send(&message)?;
}
if context.launch_args.get_root {
client.send(&Message::GetRoot)?;
}
return Ok(None);
}
let mut w = display::writer();
let app = App::new(&context)?;
w.queue(EnterAlternateScreen)?;
w.queue(cursor::Hide)?;
if context.capture_mouse {
w.queue(EnableMouseCapture)?;
}
let r = app.run(&mut w, &mut context, &config);
w.flush()?;
if context.capture_mouse {
w.queue(DisableMouseCapture)?;
}
w.queue(cursor::Show)?;
w.queue(LeaveAlternateScreen)?;
w.flush()?;
clear_resources();
r
}
fn clear_resources() {
info!("clearing resources");
crate::kitty::manager().lock().unwrap().delete_temp_files();
}
pub fn ask_authorization() -> io::Result<bool> {
let mut answer = String::new();
io::stdin().read_line(&mut answer)?;
let answer = answer.trim();
Ok(!matches!(answer, "n" | "N"))
}