add missing file mod + add lib.rs
This commit is contained in:
parent
2ca3c55092
commit
fff0d1b0dd
318
src/file.rs
Normal file
318
src/file.rs
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use mime_guess::from_path;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum FileImgMimetype {
|
||||||
|
Jpeg,
|
||||||
|
Png,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileImgMimetype {
|
||||||
|
pub fn value(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
FileImgMimetype::Jpeg => "image/jpeg",
|
||||||
|
FileImgMimetype::Png => "image/png",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Categorized files by their size in megabytes.
|
||||||
|
///
|
||||||
|
/// * TINY: [0,1[ Mb
|
||||||
|
/// * MEDIUM: [1,2[ Mb
|
||||||
|
/// * LARGE: [2,5[ Mb
|
||||||
|
/// * FAT: [5,inf[ Mb
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum FileSizeRange {
|
||||||
|
Tiny,
|
||||||
|
Medium,
|
||||||
|
Large,
|
||||||
|
Fat,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileSizeRange {
|
||||||
|
pub fn from_size(size: f64) -> Self {
|
||||||
|
if size < 1.0 {
|
||||||
|
return FileSizeRange::Tiny;
|
||||||
|
}
|
||||||
|
|
||||||
|
if size < 2.0 {
|
||||||
|
return FileSizeRange::Medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
if size < 5.0 {
|
||||||
|
return FileSizeRange::Large;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSizeRange::Fat
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
FileSizeRange::Tiny => "Tiny",
|
||||||
|
FileSizeRange::Medium => "Medium",
|
||||||
|
FileSizeRange::Fat => "Fat",
|
||||||
|
FileSizeRange::Large => "Large",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle file main attributes.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```rs
|
||||||
|
/// file = File.from_directory("dir-path", "my-file-name.png")
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct File {
|
||||||
|
pub directory: String,
|
||||||
|
pub name: String,
|
||||||
|
pub path: String,
|
||||||
|
pub mimetype: String,
|
||||||
|
pub size: f64,
|
||||||
|
pub modified: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
pub fn from_directory(directory: &str, name: &str) -> Result<Self, std::io::Error> {
|
||||||
|
let path = Path::new(directory)
|
||||||
|
.join(name)
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
|
||||||
|
let mimetype = from_path(&path)
|
||||||
|
.first()
|
||||||
|
.map_or("application/octet-stream".to_string(), |m| m.to_string());
|
||||||
|
|
||||||
|
let size = fs::metadata(&path)?.len() as f64 / 1_048_576.0;
|
||||||
|
let modified = fs::metadata(&path)?.modified()?.into();
|
||||||
|
|
||||||
|
Ok(File {
|
||||||
|
directory: directory.to_string(),
|
||||||
|
name: name.to_string(),
|
||||||
|
path,
|
||||||
|
mimetype,
|
||||||
|
size,
|
||||||
|
modified,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Group a bunch of `File`. That's all.
|
||||||
|
/// Only useful to provide number of file and the whole size in Mb quickly.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FileGroup {
|
||||||
|
files: HashMap<String, File>,
|
||||||
|
size: f64,
|
||||||
|
nb_files: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FileGroup {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileGroup {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
FileGroup {
|
||||||
|
files: HashMap::new(),
|
||||||
|
size: 0.0,
|
||||||
|
nb_files: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, file: File) {
|
||||||
|
if self.files.get(&file.path).is_none() {
|
||||||
|
self.files.insert(file.path.clone(), file.clone());
|
||||||
|
self.nb_files += 1;
|
||||||
|
self.size += file.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.nb_files
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join(&mut self, right: &FileGroup) {
|
||||||
|
for (filepath, file) in &right.files {
|
||||||
|
if self.files.get(filepath).is_none() {
|
||||||
|
self.files.insert(filepath.clone(), file.clone());
|
||||||
|
self.size += file.size;
|
||||||
|
self.nb_files += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_size(&self) -> f64 {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_size(size: f64) -> String {
|
||||||
|
if size < 1000.0 {
|
||||||
|
return format!("{:.2} Mb", size);
|
||||||
|
}
|
||||||
|
format!("{:.2} Gb", size / 1024.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_size_formatted(&self) -> String {
|
||||||
|
Self::format_size(self.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_files(&self) -> Vec<File> {
|
||||||
|
self.files.values().cloned().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a directory path grouping files by mimetype and size range.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```rs
|
||||||
|
/// directory = Directory.from_path("my-path")
|
||||||
|
/// fg = directory.get_file_group() # collect all files
|
||||||
|
///
|
||||||
|
/// // collect all tiny files of the directory
|
||||||
|
/// fg_tiny = directory.get_file_group(None, FileSizeRange::TINY)
|
||||||
|
///
|
||||||
|
/// // collect all JPEG files
|
||||||
|
/// fg_jpeg = directory.get_file_group(FileImgMimetype::JPEG, None)
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Directory {
|
||||||
|
pub path: String,
|
||||||
|
pub nb_files: usize,
|
||||||
|
pub details: HashMap<String, HashMap<FileSizeRange, FileGroup>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Directory {
|
||||||
|
pub fn from_path(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
|
if !std::path::Path::new(path).is_dir() {
|
||||||
|
return Err(format!("Directory path: {} must be a directory", path).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nb_files = 0;
|
||||||
|
let mut details: HashMap<String, HashMap<FileSizeRange, FileGroup>> = HashMap::new();
|
||||||
|
|
||||||
|
for entry in WalkDir::new(path).into_iter().filter_map(Result::ok) {
|
||||||
|
if entry.file_type().is_file() {
|
||||||
|
let file_path = entry.path().to_string_lossy().into_owned();
|
||||||
|
|
||||||
|
let file = match File::from_directory(
|
||||||
|
entry.path().parent().unwrap().to_string_lossy().as_ref(),
|
||||||
|
entry.file_name().to_string_lossy().as_ref(),
|
||||||
|
) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("error accessing {}, err: {}", file_path, e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mimetype = file.mimetype.clone();
|
||||||
|
let size_range = FileSizeRange::from_size(file.size);
|
||||||
|
|
||||||
|
details
|
||||||
|
.entry(mimetype.clone())
|
||||||
|
.or_default()
|
||||||
|
.entry(size_range)
|
||||||
|
.or_default()
|
||||||
|
.add(file);
|
||||||
|
|
||||||
|
nb_files += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Directory {
|
||||||
|
path: path.to_string(),
|
||||||
|
nb_files,
|
||||||
|
details,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.nb_files
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&self) {
|
||||||
|
let mut data = vec![format!("directory ({}) details:", self.path)];
|
||||||
|
|
||||||
|
for (mimetype, group) in &self.details {
|
||||||
|
let mut nb_files = 0;
|
||||||
|
let mut size = 0.0;
|
||||||
|
let mut to_display = vec![format!("* {}", mimetype)];
|
||||||
|
|
||||||
|
for file_range in group.keys() {
|
||||||
|
let file_group = &group[file_range];
|
||||||
|
to_display.push(format!(
|
||||||
|
"\t{:<8}{:<8}{}",
|
||||||
|
file_range.value(),
|
||||||
|
file_group.len(),
|
||||||
|
file_group.get_size_formatted()
|
||||||
|
));
|
||||||
|
nb_files += file_group.len();
|
||||||
|
size += file_group.get_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
to_display[0] = format!(
|
||||||
|
"* {} ({} files, {})",
|
||||||
|
mimetype,
|
||||||
|
nb_files,
|
||||||
|
FileGroup::format_size(size)
|
||||||
|
);
|
||||||
|
data.push(to_display.join("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{}", data.join("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_file_group(
|
||||||
|
&self,
|
||||||
|
mimetype: Option<&FileImgMimetype>,
|
||||||
|
size_range: Option<&FileSizeRange>,
|
||||||
|
) -> FileGroup {
|
||||||
|
let mut file_group = FileGroup::new();
|
||||||
|
|
||||||
|
match (mimetype, size_range) {
|
||||||
|
(None, None) => {
|
||||||
|
return self.get_all();
|
||||||
|
}
|
||||||
|
(Some(mime), None) => {
|
||||||
|
if let Some(dict_file_range) = self.details.get(mime.value()) {
|
||||||
|
for fg in dict_file_range.values() {
|
||||||
|
file_group.join(fg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(None, Some(size_range)) => {
|
||||||
|
for dict_file_range in self.details.values() {
|
||||||
|
if let Some(fg) = dict_file_range.get(size_range) {
|
||||||
|
file_group.join(fg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(mime), Some(size_range)) => {
|
||||||
|
if let Some(dict_file_range) = self.details.get(mime.value())
|
||||||
|
&& let Some(fg) = dict_file_range.get(size_range)
|
||||||
|
{
|
||||||
|
file_group.join(fg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file_group
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_all(&self) -> FileGroup {
|
||||||
|
let mut file_group = FileGroup::new();
|
||||||
|
for details in self.details.values() {
|
||||||
|
for fg in details.values() {
|
||||||
|
file_group.join(fg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_group
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/lib.rs
Normal file
3
src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
mod file;
|
||||||
|
|
||||||
|
pub use file::Directory;
|
||||||
@ -1,9 +1,7 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
|
|
||||||
mod file;
|
use rs_optimg::Directory;
|
||||||
|
|
||||||
use file::Directory;
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user