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 len_created_dir_chain: usize,
14}
15
16impl Output {
17 pub fn create_dirs(&mut self) -> Result<()> {
20 let mut dirs: Vec<&Path> = Vec::new();
21 let mut dir = &*self.dir;
22
23 while !dir.exists() {
24 dirs.push(dir);
25 match dir.parent() {
26 Some(parent) => dir = parent,
27 None => break,
28 }
29 }
30
31 for dir in dirs.iter().rev() {
32 if let Err(err) = fs::create_dir(dir) {
33 if !dir.exists() {
34 remove_created_dirs(&self.dir, dirs.len());
35 return Err(err.into());
36 }
37 }
38 }
39
40 self.len_created_dir_chain = dirs.len();
41 Ok(())
42 }
43
44 pub fn remove_created_dirs(&self) {
46 remove_created_dirs(&self.dir, self.len_created_dir_chain);
47 }
48
49 pub fn new(path: impl AsRef<Path>) -> Result<Output> {
50 let dir = new_dir(path.as_ref())?;
51 Ok(Output {
52 dir,
53 len_created_dir_chain: 0,
54 })
55 }
56
57 pub(crate) fn try_from_input(input: &Input) -> Result<Output> {
58 let new = |base: &Path| Output::new(base.join("cleaned"));
59 match input {
60 Input::Dir(d) => new(d),
61 Input::Files(xs) => new(xs
62 .get(0)
63 .and_then(|f| f.path.parent())
64 .unwrap_or(Path::new("."))),
65 }
66 }
67}
68
69impl Default for Output {
70 fn default() -> Output {
71 let dir = Path::new(".").join("cleaned");
72 Output {
73 dir: new_dir(&dir).unwrap_or(dir.into()),
74 len_created_dir_chain: 0,
75 }
76 }
77}
78
79fn remove_created_dirs(mut dir: &Path, len_created_dir_chain: usize) {
80 for _ in 0..len_created_dir_chain {
81 let _ = fs::remove_dir(dir);
82 match dir.parent() {
83 Some(parent) => dir = parent,
84 None => break,
85 }
86 }
87}
88
89fn new_dir(path: &Path) -> Result<Box<Path>> {
90 if path.is_file() {
91 Err("Is not a directory".into())
92 } else {
93 let dir: PathBuf = try_absolutize(path.into())?.components().collect();
94 Ok(ensure_long_path_prefix(dir).into())
95 }
96}
97
98fn try_absolutize(path: PathBuf) -> Result<PathBuf> {
99 #[cfg(unix)]
100 {
101 if path.starts_with("~") {
102 return Ok(path);
103 }
104 }
105
106 if path.is_absolute() {
107 Ok(path)
108 } else {
109 let mut new = env::current_dir()?;
110 new.push(path);
111 Ok(new)
112 }
113}
114
115fn ensure_long_path_prefix(path: impl Into<PathBuf>) -> PathBuf {
116 #[cfg(unix)]
117 {
118 path.into()
119 }
120
121 #[cfg(windows)]
122 {
123 let path = path.into();
124
125 if path.as_os_str().as_encoded_bytes().starts_with(b"\\\\?\\") {
126 return path;
127 }
128
129 let mut prf_path = std::ffi::OsString::from("\\\\?\\");
130 prf_path.push(path.as_os_str());
131 prf_path.into()
132 }
133}