Skip to main content

nd_cli/
lib.rs

1mod choice;
2mod cli;
3mod init;
4mod navigation;
5mod view;
6
7use clap::Parser;
8use std::{
9    error::Error,
10    io::{self, Write},
11};
12
13use choice::{Choice, commands_hint, out_of_range_message};
14use cli::{Args, Command};
15use init::init_script;
16use navigation::list_directories;
17use view::View;
18
19pub fn run() -> Result<(), Box<dyn Error>> {
20    let args = Args::parse();
21
22    if let Some(Command::Init { shell }) = args.command {
23        println!("{}", init_script(shell));
24        return Ok(());
25    }
26
27    if let Some(start_dir) = args.start_dir {
28        std::env::set_current_dir(&start_dir)?;
29    }
30
31    let use_color = !args.no_color && std::env::var_os("NO_COLOR").is_none();
32    let view = View::new(use_color);
33
34    loop {
35        view.clear();
36
37        let current_dir = std::env::current_dir()?;
38        let choices = list_directories(&current_dir, args.show_hidden)?;
39
40        view.muted(&format!("Path: {}", current_dir.display()));
41        view.muted(&format!("{}\n", commands_hint(choices.len())));
42
43        if choices.is_empty() {
44            view.muted("No subdirectories found.");
45        } else {
46            for (index, choice) in choices.iter().enumerate() {
47                view.row(index + 1, &choice.name);
48            }
49        }
50
51        let choice = prompt_choice(&view, choices.len())?;
52
53        match choice {
54            Choice::Quit => {
55                view.clear();
56                println!("{}", current_dir.display());
57                io::stdout().flush()?;
58                return Ok(());
59            }
60            Choice::Back => {
61                if let Some(parent) = current_dir.parent() {
62                    std::env::set_current_dir(parent)?;
63                }
64            }
65            Choice::Select(index) => {
66                if let Some(selected) = choices.get(index - 1) {
67                    std::env::set_current_dir(&selected.path)?;
68                } else {
69                    view.warn(&out_of_range_message(choices.len()));
70                }
71            }
72        }
73    }
74}
75
76fn prompt_choice(view: &View, max_choice: usize) -> io::Result<Choice> {
77    loop {
78        view.prompt("> ");
79        io::stdout().flush()?;
80
81        let mut raw = String::new();
82        io::stdin().read_line(&mut raw)?;
83
84        match raw.parse::<Choice>() {
85            Ok(Choice::Select(value)) if value > max_choice => {
86                view.warn(&out_of_range_message(max_choice));
87            }
88            Ok(choice) => return Ok(choice),
89            Err(_) => view.warn("Enter a number, [b], or [q]."),
90        }
91    }
92}