use std::{path::PathBuf, time::SystemTime};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum FsFileType {
TextFile,
BinaryFile,
Symlink,
Directory,
Other,
}
#[derive(Debug, Clone)]
pub struct FsMetadata {
pub size: u64,
pub modified: SystemTime,
pub permissions: u32,
pub file_type: FsFileType,
}
#[derive(Debug, Clone)]
pub struct FsDirectoryNode {
pub name: String,
pub path: PathBuf,
pub metadata: FsMetadata,
pub contents: Vec<FsNode>,
}
impl FsDirectoryNode {
pub fn get_files(&self) -> Vec<&FsFileNode> {
let mut files = Vec::new();
self.collect_files_recursive(&mut files);
files
}
fn collect_files_recursive<'a>(&'a self, files: &mut Vec<&'a FsFileNode>) {
for node in &self.contents {
match node {
FsNode::Directory(dir) => {
dir.collect_files_recursive(files);
}
FsNode::File(file_node) => {
files.push(file_node);
}
_ => {}
}
}
}
pub fn title(&self) -> String {
let mut path = self.path.as_path();
let git_dir = ".git";
while let Some(parent) = path.parent() {
if parent.join(git_dir).is_dir() {
let repo_name = parent
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("repo");
return match self.path.strip_prefix(parent) {
Ok(rel_path) => {
let rel_path_str = rel_path.to_str().unwrap_or(&self.name);
if rel_path_str.is_empty() {
format!("{}'s {}", repo_name, self.name)
} else {
format!("{}'s {}", repo_name, rel_path_str)
}
}
Err(_) => format!("{}'s {}", repo_name, self.name),
};
}
path = parent;
}
self.name.to_string()
}
}
#[derive(Debug, Clone)]
pub struct FsFileNode {
pub name: String,
pub path: PathBuf,
pub metadata: FsMetadata,
pub content: Option<String>,
pub lines: Option<usize>,
pub chars: Option<usize>,
pub token_count: Option<usize>, }
impl FsFileNode {
pub fn extension(&self) -> &str {
self.path
.extension()
.and_then(|ext| ext.to_str())
.unwrap_or("txt")
}
}
#[derive(Debug, Clone)]
pub struct FsBinaryNode {
pub name: String,
pub path: PathBuf,
pub metadata: FsMetadata,
}
#[derive(Debug, Clone)]
pub struct FsSymlinkNode {
pub name: String,
pub path: PathBuf,
pub metadata: FsMetadata,
pub target: String,
pub target_is_dir: Option<bool>,
pub target_exists: Option<bool>,
}
#[derive(Debug, Clone)]
pub enum FsNode {
Directory(FsDirectoryNode),
File(FsFileNode),
Binary(FsBinaryNode),
Symlink(FsSymlinkNode),
}
impl FsNode {
pub fn path(&self) -> &PathBuf {
match self {
FsNode::Directory(d) => &d.path,
FsNode::File(f) => &f.path,
FsNode::Binary(b) => &b.path,
FsNode::Symlink(s) => &s.path,
}
}
pub fn name(&self) -> &str {
match self {
FsNode::Directory(d) => &d.name,
FsNode::File(f) => &f.name,
FsNode::Binary(b) => &b.name,
FsNode::Symlink(s) => &s.name,
}
}
pub fn metadata(&self) -> &FsMetadata {
match self {
FsNode::Directory(d) => &d.metadata,
FsNode::File(f) => &f.metadata,
FsNode::Binary(b) => &b.metadata,
FsNode::Symlink(s) => &s.metadata,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct FileDetail {
pub lines: usize,
pub chars: usize,
}