use colored::*;
use std::fs::{create_dir_all, OpenOptions};
use std::io::{BufRead, ErrorKind::AlreadyExists, Lines};
use std::iter::Iterator;
use std::path::{Path, PathBuf};
use super::{PathKind::*, *};
use quick_error::ResultExt;
pub fn get_entry(mut entry: &str) -> (i32, &str) {
let mut depth = 0;
loop {
match either!(
entry.strip_prefix(" "),
entry.strip_prefix("└── "),
entry.strip_prefix("├── "),
entry.strip_prefix("│ "),
entry.strip_prefix("│\u{a0}\u{a0} ")
) {
Some(suffix) => {
entry = suffix;
depth += 1;
}
None => return (depth, entry),
}
}
}
pub fn touch_file(path: &Path) -> Result<()> {
match OpenOptions::new().write(true).create_new(true).open(path) {
Ok(_) => Ok(()),
Err(err) => match err.kind() {
AlreadyExists => Ok(()),
_ => Err(err).context(CreateFile.on(path))?,
},
}
}
pub fn touch_directory(path: &Path) -> Result<()> {
Ok(create_dir_all(path).context(CreateDirectory.on(path))?)
}
pub fn create_path(
path: &Path,
kind: PathKind,
options: UntreeOptions,
) -> Result<()> {
let name = path.to_str().unwrap_or("<unprintable>");
match (options.is_verbose(), kind) {
(false, _) => {} (_, FilePath) => {
println!("{} {}", "touch".bold().green(), name.bold().white())
}
(_, Directory) => {
println!("{} -p {}", "mkdir".bold().green(), name.bold().blue())
}
}
match (options.dry_run, kind) {
(true, _) => Ok(()), (_, FilePath) => touch_file(path),
(_, Directory) => touch_directory(path),
}
}
fn normalize_path(path: &Path) -> PathBuf {
let mut result = PathBuf::new();
let mut go_back = 0;
for component in path.components() {
match component.as_os_str().to_str() {
Some(".") => {}
Some("..") => {
if !result.pop() {
go_back += 1;
}
}
_ => result.push(component),
}
}
if go_back > 0 {
let mut prefix = PathBuf::new();
for _ in 0..go_back {
prefix.push("..");
}
prefix.push(result);
result = prefix;
}
result
}
pub fn create_tree(
directory: impl Into<PathBuf>,
mut lines: Lines<impl BufRead>,
options: UntreeOptions,
) -> Result<()> {
let mut path = directory.into();
let mut old_depth = 0;
if let Some(result) = lines.next() {
let line = result?;
let (depth, filename) = get_entry(line.as_ref());
path.push(filename);
path = normalize_path(path.as_path());
old_depth = depth;
}
for result in lines {
let line = result?;
if line.is_empty() {
break;
}
let (depth, filename) = get_entry(line.as_ref());
if depth <= old_depth {
create_path(path.as_path(), FilePath, options)?;
for _ in depth..old_depth {
path.pop();
}
path.set_file_name(filename);
} else {
create_path(path.as_path(), Directory, options)?;
path.push(filename);
}
old_depth = depth;
}
create_path(path.as_path(), FilePath, options)
}