Compare commits
2 Commits
3ae188be26
...
5bc0c302f3
Author | SHA1 | Date |
---|---|---|
Nicholas Hope | 5bc0c302f3 | |
Nicholas Hope | 98a3ef9a47 |
|
@ -0,0 +1,84 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Unit {
|
||||
Byte,
|
||||
|
||||
Kilo,
|
||||
Kibi,
|
||||
|
||||
Mega,
|
||||
Mibi,
|
||||
|
||||
Giga,
|
||||
Gibi,
|
||||
|
||||
Tera,
|
||||
Tibi,
|
||||
}
|
||||
impl Unit {
|
||||
pub fn parse(s: &str) -> Result<Self, String> {
|
||||
let s = s.to_lowercase();
|
||||
match s.as_str() {
|
||||
"b" => Ok(Self::Byte),
|
||||
"k" | "kb" => Ok(Self::Kilo),
|
||||
"ki" => Ok(Self::Kibi),
|
||||
"m" | "mb" => Ok(Self::Mega),
|
||||
"mi" => Ok(Self::Mibi),
|
||||
"g" | "gb" => Ok(Self::Giga),
|
||||
"gi" => Ok(Self::Gibi),
|
||||
"t" | "tb" => Ok(Self::Tera),
|
||||
"ti" => Ok(Self::Tibi),
|
||||
_ => Err(s),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert(self, n: u64) -> String {
|
||||
format!("{}{}", n/self.integer_value(), self.units_pretty())
|
||||
}
|
||||
|
||||
const fn units_pretty(self) -> &'static str {
|
||||
match self {
|
||||
Self::Byte => "",
|
||||
Self::Kilo => " K",
|
||||
Self::Kibi => " Ki",
|
||||
Self::Mega => " M",
|
||||
Self::Mibi => " Mi",
|
||||
Self::Giga => " G",
|
||||
Self::Gibi => " Gi",
|
||||
Self::Tera => " T",
|
||||
Self::Tibi => " Ti",
|
||||
}
|
||||
}
|
||||
|
||||
const fn integer_value(self) -> u64 {
|
||||
match self {
|
||||
Self::Byte => 1,
|
||||
Self::Kilo => 1_000,
|
||||
Self::Kibi => 1_024,
|
||||
Self::Mega => 1_000_000,
|
||||
Self::Mibi => 1_048_576,
|
||||
Self::Giga => 1_000_000_000,
|
||||
Self::Gibi => 1_073_741_824,
|
||||
Self::Tera => 1_000_000_000_000,
|
||||
Self::Tibi => 1_099_511_627_776,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Display for Unit {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let s = match self {
|
||||
Self::Byte => "b",
|
||||
Self::Kilo => "K",
|
||||
Self::Kibi => "Ki",
|
||||
Self::Mega => "M",
|
||||
Self::Mibi => "Mi",
|
||||
Self::Giga => "G",
|
||||
Self::Gibi => "Gi",
|
||||
Self::Tera => "T",
|
||||
Self::Tibi => "Ti",
|
||||
};
|
||||
|
||||
f.write_str(s)
|
||||
}
|
||||
}
|
91
src/walk.rs
91
src/walk.rs
|
@ -1,91 +0,0 @@
|
|||
use rayon::prelude::*;
|
||||
|
||||
use std::sync::mpsc::{Sender, channel};
|
||||
use std::fs::{read_dir, DirEntry};
|
||||
use std::io::Error;
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitCode;
|
||||
|
||||
use crate::Args;
|
||||
use crate::directory::Directory;
|
||||
|
||||
pub fn walk<'a>(args: Args) -> Result<Directory, ExitCode> {
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let mut total = 0;
|
||||
for entry in read_dir(&args.path).unwrap() {
|
||||
let entry = match entry {
|
||||
Ok(e) => e,
|
||||
Err(e) => {
|
||||
if !args.minimal {
|
||||
eprintln!("unable to open {}: {e}", args.path);
|
||||
}
|
||||
if args.persistant {
|
||||
continue;
|
||||
} else {
|
||||
return Err(ExitCode::FAILURE);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
total += match total_entry(entry, &args, &tx) {
|
||||
Ok(t) => t,
|
||||
Err((path, error)) => {
|
||||
if !args.minimal {
|
||||
eprintln!("error opening {}: {error}", path.display());
|
||||
}
|
||||
if args.persistant {
|
||||
continue;
|
||||
} else {
|
||||
return Err(ExitCode::FAILURE);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
// drop this to close the channel, so that into_iter() can end
|
||||
drop(tx);
|
||||
|
||||
let mut fs = Directory::make(args.path, total);
|
||||
fs.extend(rx);
|
||||
|
||||
Ok(fs)
|
||||
}
|
||||
|
||||
fn total_entry(entry: DirEntry, args: &Args, printer: &Sender<(PathBuf, u64)>) -> Result<u64, (PathBuf, Error)> {
|
||||
let path = entry.path();
|
||||
|
||||
match path.read_dir() {
|
||||
Ok(dir) => {
|
||||
let result = dir.par_bridge()
|
||||
.filter_map(Result::ok)
|
||||
.map(|entry| total_entry(entry, args, printer))
|
||||
.reduce(|| Ok(0), reduce_once);
|
||||
|
||||
if let Ok(size) = result {
|
||||
if !args.minimal && !args.total_only {
|
||||
let _ = printer.send((path, size));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
Err(_) if path.is_file() => {
|
||||
let size = unsafe { path.metadata().unwrap_unchecked() }.len();
|
||||
if !args.minimal && !args.total_only {
|
||||
let _ = printer.send((path, size));
|
||||
}
|
||||
return Ok(size);
|
||||
},
|
||||
Err(e) => Err((path, e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce_once<E>(accum: Result<u64, E>, this: Result<u64, E>) -> Result<u64, E> {
|
||||
// reduction function for total_entry():
|
||||
// short-circuit Errs, propagate Oks
|
||||
// generic bc I'm lazy
|
||||
match (accum, this) {
|
||||
(Ok(n1), Ok(n2)) => Ok(n1 + n2),
|
||||
(Err(e), _) | (_, Err(e)) => Err(e),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue