nushell/crates/nu-command/src/filesystem/util.rs
Michael Angerman 95cd9dd2b2
move BufferedReader out of nu-command (#7697)
src/main.rs has a dependency on BufferedReader
which is currently located in nu_command.

I am moving BufferedReader to a more relevant
location (crate) which will allow / eliminate main's dependency
on nu_command in a benchmark / testing environment...

now that @rgwood  has landed benches I want
to start experimenting with benchmarks related
to the parser.

For benchmark purposes when dealing with parsing
you need a very simple set of commands that show
how well the parser is doing, in other words
just the core commands... Not all of nu_command...

Having a smaller nu binary when running the benchmark CI
would enable building nushell quickly, yet still show us
how well the parser is performing...

Once this PR lands the only dependency main will have
on nu_command is create_default_context ---
meaning for benchmark purposes we can swap in a tiny
crate of commands instead of the gigantic nu_command
which has its "own" create_default_context...

It will also enable other crates going forward to
use BufferedReader.  Right now it is not accessible
to other lower level crates because it is located in a
"top of the stack crate".
2023-01-06 15:22:17 -08:00

135 lines
3.3 KiB
Rust

use std::path::{Path, PathBuf};
use nu_engine::env::current_dir_str;
use nu_path::canonicalize_with;
use nu_protocol::engine::{EngineState, Stack};
use nu_protocol::ShellError;
use dialoguer::Input;
use std::error::Error;
#[derive(Default)]
pub struct FileStructure {
pub resources: Vec<Resource>,
}
impl FileStructure {
pub fn new() -> FileStructure {
FileStructure { resources: vec![] }
}
pub fn paths_applying_with<F>(
&mut self,
to: F,
) -> Result<Vec<(PathBuf, PathBuf)>, Box<dyn std::error::Error>>
where
F: Fn((PathBuf, usize)) -> Result<(PathBuf, PathBuf), Box<dyn std::error::Error>>,
{
self.resources
.iter()
.map(|f| (PathBuf::from(&f.location), f.at))
.map(to)
.collect()
}
pub fn walk_decorate(
&mut self,
start_path: &Path,
engine_state: &EngineState,
stack: &Stack,
) -> Result<(), ShellError> {
self.resources = Vec::<Resource>::new();
self.build(start_path, 0, engine_state, stack)?;
self.resources.sort();
Ok(())
}
fn build(
&mut self,
src: &Path,
lvl: usize,
engine_state: &EngineState,
stack: &Stack,
) -> Result<(), ShellError> {
let source = canonicalize_with(src, current_dir_str(engine_state, stack)?)?;
if source.is_dir() {
for entry in std::fs::read_dir(src)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
self.build(&path, lvl + 1, engine_state, stack)?;
}
self.resources.push(Resource {
location: path.to_path_buf(),
at: lvl,
});
}
} else {
self.resources.push(Resource {
location: source,
at: lvl,
});
}
Ok(())
}
}
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Resource {
pub at: usize,
pub location: PathBuf,
}
impl Resource {}
pub fn try_interaction(
interactive: bool,
prompt: String,
) -> (Result<Option<bool>, Box<dyn Error>>, bool) {
let interaction = if interactive {
match get_interactive_confirmation(prompt) {
Ok(i) => Ok(Some(i)),
Err(e) => Err(e),
}
} else {
Ok(None)
};
let confirmed = match interaction {
Ok(maybe_input) => maybe_input.unwrap_or(false),
Err(_) => false,
};
(interaction, confirmed)
}
#[allow(dead_code)]
fn get_interactive_confirmation(prompt: String) -> Result<bool, Box<dyn Error>> {
let input = Input::new()
.with_prompt(prompt)
.validate_with(|c_input: &String| -> Result<(), String> {
if c_input.len() == 1
&& (c_input == "y" || c_input == "Y" || c_input == "n" || c_input == "N")
{
Ok(())
} else if c_input.len() > 1 {
Err("Enter only one letter (Y/N)".to_string())
} else {
Err("Input not valid".to_string())
}
})
.default("Y/N".into())
.interact_text()?;
if input == "y" || input == "Y" {
Ok(true)
} else {
Ok(false)
}
}