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::File(f) => new(f.path.parent().unwrap_or(Path::new("."))),
62 }
63 }
64}
65
66impl Default for Output {
67 fn default() -> Output {
68 let dir = Path::new(".").join("cleaned");
69 Output {
70 dir: new_dir(&dir).unwrap_or(dir.into()),
71 len_created_dir_chain: 0,
72 }
73 }
74}
75
76fn remove_created_dirs(mut dir: &Path, len_created_dir_chain: usize) {
77 for _ in 0..len_created_dir_chain {
78 let _ = fs::remove_dir(dir);
79 match dir.parent() {
80 Some(parent) => dir = parent,
81 None => break,
82 }
83 }
84}
85
86fn new_dir(path: &Path) -> Result<Box<Path>> {
87 if path.is_file() {
88 Err("Is not a directory".into())
89 } else {
90 let dir: PathBuf = try_absolutize(path.into())?.components().collect();
91 Ok(ensure_long_path_prefix(dir).into())
92 }
93}
94
95fn try_absolutize(path: PathBuf) -> Result<PathBuf> {
96 #[cfg(unix)]
97 {
98 if path.starts_with("~") {
99 return Ok(path);
100 }
101 }
102
103 if path.is_absolute() {
104 Ok(path)
105 } else {
106 let mut new = env::current_dir()?;
107 new.push(path);
108 Ok(new)
109 }
110}
111
112fn ensure_long_path_prefix(path: impl Into<PathBuf>) -> PathBuf {
113 #[cfg(unix)]
114 {
115 path.into()
116 }
117
118 #[cfg(windows)]
119 {
120 let path = path.into();
121
122 if path.as_os_str().as_encoded_bytes().starts_with(b"\\\\?\\") {
123 return path;
124 }
125
126 let mut prf_path = std::ffi::OsString::from("\\\\?\\");
127 prf_path.push(path.as_os_str());
128 prf_path.into()
129 }
130}