use fast_fs::nav::{ActionResult, Browser, BrowserConfig, KeyInput};
use std::io::{self, Write};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = std::env::args().nth(1).unwrap_or_else(|| ".".to_string());
println!("=== fast-fs File Browser Demo ===");
println!("Starting at: {}\n", path);
let config = BrowserConfig::open_dialog()
.with_initial_path(&path)
.with_show_parent_entry(true);
let mut browser = Browser::at_path(&path, config).await?;
loop {
print!("\x1B[2J\x1B[1;1H"); render(&browser);
print!("\nCommand (j/k/Enter/h/Space/./s/q): ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let input = input.trim();
let key = match input {
"j" => KeyInput::Down,
"k" => KeyInput::Up,
"l" | "" => KeyInput::Enter, "h" => KeyInput::Backspace,
" " => KeyInput::Char(' '),
"." => KeyInput::Char('.'),
"s" => KeyInput::Char('s'),
"g" => KeyInput::Char('g'),
"G" => KeyInput::Char('G'),
"q" => break,
c if c.len() == 1 => {
let ch = c.chars().next().unwrap();
if browser.jump_to_char(ch) {
continue;
}
KeyInput::Char(ch)
}
_ => continue,
};
match browser.handle_key(key).await {
ActionResult::Done => {}
ActionResult::DirectoryChanged => {
println!("\nChanged to: {}", browser.current_path().display());
}
ActionResult::FileSelected(path) => {
println!("\n\nSelected file: {}", path.display());
println!("Press Enter to continue...");
let mut buf = String::new();
io::stdin().read_line(&mut buf)?;
}
ActionResult::Clipboard(state) => {
println!("\n{}", state.message());
}
ActionResult::NeedsConfirmation(op) => {
println!("\nConfirm: {} (y/n)", op.message());
let mut buf = String::new();
io::stdin().read_line(&mut buf)?;
let confirmed = buf.trim().to_lowercase() == "y";
browser.resolve_confirmation(confirmed).await?;
}
ActionResult::NeedsInput(req) => {
print!("\n{} ", req.prompt());
io::stdout().flush()?;
let mut buf = String::new();
io::stdin().read_line(&mut buf)?;
let input = buf.trim();
if !input.is_empty() {
browser.complete_input(input).await?;
} else {
browser.cancel_input();
}
}
ActionResult::Unhandled => {}
}
}
println!("\nGoodbye!");
Ok(())
}
fn render(browser: &Browser) {
println!("Directory: {}", browser.current_path().display());
println!(
"Sort: {:?} | Hidden: {} | Selected: {}",
browser.files().sort_by(),
browser.config().show_hidden,
browser.selection().count()
);
println!("{}", "-".repeat(60));
let viewport_height = 15;
let range = browser.visible_range(viewport_height);
let range_start = range.start;
let range_end = range.end;
for i in range {
if let Some(entry) = browser.files().get(i) {
let cursor = if i == browser.cursor() { ">" } else { " " };
let selected = if browser.selection().is_selected(&entry.path) {
"*"
} else {
" "
};
let kind = if entry.is_dir { "/" } else { " " };
let hidden = if entry.is_hidden { "." } else { " " };
println!("{}{}{}{} {}", cursor, selected, hidden, kind, entry.name);
}
}
println!("{}", "-".repeat(60));
println!(
"Total: {} | Showing: {}-{} of {}",
browser.total_count(),
range_start + 1,
range_end,
browser.filtered_count()
);
}