nushell/crates/nu-command/src/platform/dir_info.rs
JT 6cdfee3573
Move Value to helpers, separate span call (#10121)
# Description

As part of the refactor to split spans off of Value, this moves to using
helper functions to create values, and using `.span()` instead of
matching span out of Value directly.

Hoping to get a few more helping hands to finish this, as there are a
lot of commands to update :)

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
Co-authored-by: WindSoilder <windsoilder@outlook.com>
2023-09-03 07:27:29 -07:00

244 lines
6.3 KiB
Rust

use filesize::file_real_size_fast;
use nu_glob::Pattern;
use nu_protocol::{record, ShellError, Span, Value};
use std::path::PathBuf;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct DirBuilder {
pub tag: Span,
pub min: Option<u64>,
pub deref: bool,
pub exclude: Option<Pattern>,
pub all: bool,
}
impl DirBuilder {
pub fn new(
tag: Span,
min: Option<u64>,
deref: bool,
exclude: Option<Pattern>,
all: bool,
) -> DirBuilder {
DirBuilder {
tag,
min,
deref,
exclude,
all,
}
}
}
#[derive(Debug, Clone)]
pub struct DirInfo {
dirs: Vec<DirInfo>,
files: Vec<FileInfo>,
errors: Vec<ShellError>,
size: u64,
blocks: u64,
path: PathBuf,
tag: Span,
}
#[derive(Debug, Clone)]
pub struct FileInfo {
path: PathBuf,
size: u64,
blocks: Option<u64>,
tag: Span,
}
impl FileInfo {
pub fn new(path: impl Into<PathBuf>, deref: bool, tag: Span) -> Result<Self, ShellError> {
let path = path.into();
let m = if deref {
std::fs::metadata(&path)
} else {
std::fs::symlink_metadata(&path)
};
match m {
Ok(d) => {
let block_size = file_real_size_fast(&path, &d).ok();
Ok(FileInfo {
path,
blocks: block_size,
size: d.len(),
tag,
})
}
Err(e) => Err(e.into()),
}
}
}
impl DirInfo {
pub fn new(
path: impl Into<PathBuf>,
params: &DirBuilder,
depth: Option<u64>,
ctrl_c: Option<Arc<AtomicBool>>,
) -> Self {
let path = path.into();
let mut s = Self {
dirs: Vec::new(),
errors: Vec::new(),
files: Vec::new(),
size: 0,
blocks: 0,
tag: params.tag,
path,
};
match std::fs::metadata(&s.path) {
Ok(d) => {
s.size = d.len(); // dir entry size
s.blocks = file_real_size_fast(&s.path, &d).ok().unwrap_or(0);
}
Err(e) => s = s.add_error(e.into()),
};
match std::fs::read_dir(&s.path) {
Ok(d) => {
for f in d {
if nu_utils::ctrl_c::was_pressed(&ctrl_c) {
break;
}
match f {
Ok(i) => match i.file_type() {
Ok(t) if t.is_dir() => {
s = s.add_dir(i.path(), depth, params, ctrl_c.clone())
}
Ok(_t) => s = s.add_file(i.path(), params),
Err(e) => s = s.add_error(e.into()),
},
Err(e) => s = s.add_error(e.into()),
}
}
}
Err(e) => s = s.add_error(e.into()),
}
s
}
fn add_dir(
mut self,
path: impl Into<PathBuf>,
mut depth: Option<u64>,
params: &DirBuilder,
ctrl_c: Option<Arc<AtomicBool>>,
) -> Self {
if let Some(current) = depth {
if let Some(new) = current.checked_sub(1) {
depth = Some(new);
} else {
return self;
}
}
let d = DirInfo::new(path, params, depth, ctrl_c);
self.size += d.size;
self.blocks += d.blocks;
self.dirs.push(d);
self
}
fn add_file(mut self, f: impl Into<PathBuf>, params: &DirBuilder) -> Self {
let f = f.into();
let include = params
.exclude
.as_ref()
.map_or(true, |x| !x.matches_path(&f));
if include {
match FileInfo::new(f, params.deref, self.tag) {
Ok(file) => {
let inc = params.min.map_or(true, |s| file.size >= s);
if inc {
self.size += file.size;
self.blocks += file.blocks.unwrap_or(0);
if params.all {
self.files.push(file);
}
}
}
Err(e) => self = self.add_error(e),
}
}
self
}
fn add_error(mut self, e: ShellError) -> Self {
self.errors.push(e);
self
}
pub fn get_size(&self) -> u64 {
self.size
}
}
impl From<DirInfo> for Value {
fn from(d: DirInfo) -> Self {
// if !d.errors.is_empty() {
// let v = d
// .errors
// .into_iter()
// .map(move |e| Value::Error { error: e })
// .collect::<Vec<Value>>();
// cols.push("errors".into());
// vals.push(Value::List {
// vals: v,
// span: d.tag,
// })
// }
Value::record(
record! {
"path" => Value::string(d.path.display().to_string(), d.tag),
"apparent" => Value::filesize(d.size as i64, d.tag),
"physical" => Value::filesize(d.blocks as i64, d.tag),
"directories" => value_from_vec(d.dirs, d.tag),
"files" => value_from_vec(d.files, d.tag)
},
d.tag,
)
}
}
impl From<FileInfo> for Value {
fn from(f: FileInfo) -> Self {
// cols.push("errors".into());
// vals.push(Value::nothing(Span::unknown()));
Value::record(
record! {
"path" => Value::string(f.path.display().to_string(), f.tag),
"apparent" => Value::filesize(f.size as i64, f.tag),
"physical" => Value::filesize(f.blocks.unwrap_or(0) as i64, f.tag),
"directories" => Value::nothing(Span::unknown()),
"files" => Value::nothing(Span::unknown()),
},
f.tag,
)
}
}
fn value_from_vec<V>(vec: Vec<V>, tag: Span) -> Value
where
V: Into<Value>,
{
if vec.is_empty() {
Value::nothing(tag)
} else {
let values = vec.into_iter().map(Into::into).collect::<Vec<Value>>();
Value::list(values, tag)
}
}