99 lines
2.3 KiB
Rust
99 lines
2.3 KiB
Rust
use std::{
|
|
collections::HashSet,
|
|
fs::{self, OpenOptions},
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
use serde::Serialize;
|
|
use walkdir::{DirEntry, WalkDir};
|
|
|
|
const DEPTH: usize = 5;
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct Directory {
|
|
name: String,
|
|
path: String,
|
|
parent: Option<String>,
|
|
is_root: bool,
|
|
}
|
|
|
|
fn is_readable_and_writable(path: &Path) -> bool {
|
|
if fs::read_dir(path).is_err() {
|
|
return false;
|
|
}
|
|
|
|
let test_file = path.join(".test_rw_check");
|
|
match OpenOptions::new()
|
|
.write(true)
|
|
.create_new(true)
|
|
.open(&test_file)
|
|
{
|
|
Ok(_) => {
|
|
let _ = fs::remove_file(&test_file);
|
|
true
|
|
}
|
|
Err(_) => false,
|
|
}
|
|
}
|
|
|
|
fn is_hidden(entry: &DirEntry) -> bool {
|
|
let file_name_hidden = entry
|
|
.file_name()
|
|
.to_str()
|
|
.map(|s| s.starts_with('.'))
|
|
.unwrap_or(false);
|
|
|
|
#[cfg(windows)]
|
|
{
|
|
use std::os::windows::fs::MetadataExt;
|
|
use winapi::um::winnt::FILE_ATTRIBUTE_HIDDEN;
|
|
|
|
if let Ok(metadata) = entry.metadata() {
|
|
let attrs = metadata.file_attributes();
|
|
return file_name_hidden || (attrs & FILE_ATTRIBUTE_HIDDEN != 0);
|
|
}
|
|
}
|
|
|
|
file_name_hidden
|
|
}
|
|
|
|
pub fn get_readable_writable_dirs(root: &Path) -> Vec<Directory> {
|
|
let mut dirs = Vec::new();
|
|
let mut all_paths = HashSet::new();
|
|
|
|
for entry in WalkDir::new(root)
|
|
.follow_links(false)
|
|
.max_depth(DEPTH)
|
|
.into_iter()
|
|
.filter_entry(|e| !is_hidden(e))
|
|
.filter_map(Result::ok)
|
|
.filter(|e| e.file_type().is_dir())
|
|
{
|
|
let path = entry.path();
|
|
|
|
if is_readable_and_writable(path) {
|
|
all_paths.insert(path.to_path_buf());
|
|
}
|
|
}
|
|
|
|
for path in &all_paths {
|
|
let parent = path.parent().map(|p| p.to_string_lossy().to_string());
|
|
let is_root = parent
|
|
.as_ref()
|
|
.map(|p| !all_paths.contains(&PathBuf::from(p)))
|
|
.unwrap_or(true);
|
|
|
|
dirs.push(Directory {
|
|
name: path
|
|
.file_name()
|
|
.map(|f| f.to_string_lossy().to_string())
|
|
.unwrap_or_else(|| "/".to_string()),
|
|
path: path.to_string_lossy().to_string(),
|
|
parent,
|
|
is_root,
|
|
});
|
|
}
|
|
|
|
dirs
|
|
}
|