use super::{error::FsFmtResult, opts};
use crate::fs::nodes;
use askama::Template;
use chrono::{DateTime, Local};
use std::fmt::Write;
#[derive(Template)]
#[template(path = "text-template.md")]
pub struct FsTextFmt<'a> {
root: &'a nodes::FsDirectoryNode,
opts: &'a opts::FsFmtOpts,
}
impl<'a> FsTextFmt<'a> {
pub fn new(root: &'a nodes::FsDirectoryNode, opts: &'a opts::FsFmtOpts) -> Self {
Self { root, opts }
}
pub fn get_tree_outline(&self) -> FsFmtResult<String> {
let mut buf = ".".to_string();
self.write_tree_outline(&mut buf, self.root, 0)?;
Ok(buf)
}
pub fn format_metadata(&self, meta: &nodes::FsMetadata) -> String {
let mut parts = Vec::new();
if self.opts.show_permissions {
parts.push(format!("perms={:04o}", meta.permissions & 0o7777));
}
if self.opts.show_size && meta.file_type != nodes::FsFileType::Directory {
parts.push(format!("size={}", meta.size));
}
if self.opts.show_modified {
let datetime: DateTime<Local> = meta.modified.into();
parts.push(format!("modified={}", datetime.format("%Y-%m-%d %H:%M:%S")));
}
if !parts.is_empty() {
format!(" `({})`", parts.join(", "))
} else {
String::new()
}
}
fn write_tree_outline<W: Write>(
&self,
writer: &mut W,
node: &nodes::FsDirectoryNode,
depth: usize,
) -> FsFmtResult {
let mut dirs = Vec::new();
let mut files = Vec::new();
for child in &node.contents {
match child {
nodes::FsNode::Directory(dir) => dirs.push(dir),
_ => files.push(child),
}
}
dirs.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
files.sort_by_key(|a| a.name().to_lowercase());
let total_items = dirs.len() + files.len();
let mut item_count = 0;
for dir in dirs {
item_count += 1;
let is_last = item_count == total_items;
self.write_tree_prefix(writer, depth, is_last)?;
writeln!(writer, "{}/", dir.name)?;
self.write_tree_outline(writer, dir, depth + 1)?;
}
for file in files {
item_count += 1;
let is_last = item_count == total_items;
self.write_tree_prefix(writer, depth, is_last)?;
match file {
nodes::FsNode::File(f) => writeln!(writer, "{}", f.name)?,
nodes::FsNode::Binary(b) => writeln!(writer, "{} [Binary]", b.name)?,
nodes::FsNode::Symlink(s) => {
let target_info = format!(" -> {}", s.target);
writeln!(writer, "{} [Symlink]{}", s.name, target_info)?;
}
_ => {}
}
}
Ok(())
}
fn write_tree_prefix<W: Write>(
&self,
writer: &mut W,
depth: usize,
is_last: bool,
) -> FsFmtResult {
for _ in 0..depth {
write!(writer, "│ ")?;
}
if depth > 0 {
if is_last {
write!(writer, "└── ")?;
} else {
write!(writer, "├── ")?;
}
}
Ok(())
}
}