fb2_clean/config/
output.rs1use crate::{Input, Result};
2use std::{
3 env, fs,
4 path::{Path, PathBuf},
5};
6
7#[derive(Clone, Debug, PartialEq)]
9pub struct Output {
10 pub dir: Box<Path>,
12 pub created_dirs: Vec<Box<Path>>,
14}
15
16impl Output {
17 pub fn create_dirs(&mut self) -> Result<()> {
20 let mut dirs = Vec::<Box<Path>>::new();
21 let mut dir = &*self.dir;
22
23 while !dir.exists() {
24 dirs.push(dir.into());
25
26 if let Some(parent) = dir.parent() {
27 dir = parent;
28 } else {
29 break;
30 }
31 }
32
33 for dir in dirs.iter().rev() {
34 if let Err(err) = fs::create_dir(dir) {
35 if !dir.exists() {
36 remove_dir_chain(&dirs);
37 return Err(err.into());
38 }
39 }
40 }
41
42 self.created_dirs = dirs;
43 Ok(())
44 }
45
46 pub fn remove_created_dirs(&self) {
48 remove_dir_chain(&self.created_dirs)
49 }
50
51 pub(crate) fn new(path: impl AsRef<Path>) -> Result<Output> {
52 let dir = new_dir(path.as_ref())?;
53 Ok(Output {
54 dir,
55 created_dirs: Vec::new(),
56 })
57 }
58
59 pub(crate) fn try_from_input(input: &Input) -> Result<Output> {
60 let new = |base: &Path| Output::new(base.join("cleaned"));
61 match input {
62 Input::Dir(d) => new(d),
63 Input::File(f) => new(f.path.parent().unwrap_or(Path::new("."))),
64 }
65 }
66}
67
68fn remove_dir_chain(dirs: &Vec<Box<Path>>) {
69 for dir in dirs {
70 let _ = fs::remove_dir(dir);
71 }
72}
73
74fn new_dir(path: &Path) -> Result<Box<Path>> {
75 if path.is_file() {
76 Err("Is not a directory".into())
77 } else {
78 let dir: PathBuf = try_absolutize(path.into())?.components().collect();
79 Ok(ensure_long_path_prefix(dir).into())
80 }
81}
82
83fn try_absolutize(path: PathBuf) -> Result<PathBuf> {
84 #[cfg(unix)]
85 {
86 if path.starts_with("~") {
87 return Ok(path);
88 }
89 }
90
91 if path.is_absolute() {
92 Ok(path)
93 } else {
94 let mut new = env::current_dir()?;
95 new.push(path);
96 Ok(new)
97 }
98}
99
100fn ensure_long_path_prefix(path: impl Into<PathBuf>) -> PathBuf {
101 #[cfg(unix)]
102 {
103 path.into()
104 }
105
106 #[cfg(windows)]
107 {
108 let path = path.into();
109
110 if path.as_os_str().as_encoded_bytes().starts_with(b"\\\\?\\") {
111 return path;
112 }
113
114 let mut prf_path = std::ffi::OsString::from("\\\\?\\");
115 prf_path.push(path.as_os_str());
116 prf_path.into()
117 }
118}