1use std::fs::{self, File};
4use std::path::PathBuf;
5
6use tokio::sync::mpsc;
7
8use crate::progress::{OperationComplete, OperationProgress, OperationType};
9use crate::rename::validate_filename;
10use crate::{OPERATION_CHANNEL_SIZE, OperationError};
11
12#[derive(Debug)]
14pub enum CreateResult {
15 Progress(OperationProgress),
17 Complete(OperationComplete),
19}
20
21pub fn start_create_file(path: PathBuf) -> mpsc::Receiver<CreateResult> {
23 let (tx, rx) = mpsc::channel(OPERATION_CHANNEL_SIZE);
24
25 tokio::spawn(async move {
26 create_file_impl(path, tx).await;
27 });
28
29 rx
30}
31
32pub fn start_create_directory(path: PathBuf) -> mpsc::Receiver<CreateResult> {
34 let (tx, rx) = mpsc::channel(OPERATION_CHANNEL_SIZE);
35
36 tokio::spawn(async move {
37 create_directory_impl(path, tx).await;
38 });
39
40 rx
41}
42
43async fn create_file_impl(path: PathBuf, tx: mpsc::Sender<CreateResult>) {
45 let mut progress = OperationProgress::new(OperationType::CreateFile, 1, 0);
46 progress.set_current_file(Some(path.clone()));
47
48 let _ = tx.send(CreateResult::Progress(progress.clone())).await;
49
50 if let Some(name) = path.file_name().and_then(|n| n.to_str())
52 && let Err(e) = validate_filename(name)
53 {
54 progress.add_error(OperationError::new(path.clone(), e));
55 let _ = tx
56 .send(CreateResult::Complete(OperationComplete {
57 operation_type: OperationType::CreateFile,
58 succeeded: 0,
59 failed: 1,
60 bytes_processed: 0,
61 errors: progress.errors,
62 }))
63 .await;
64 return;
65 }
66
67 if path.exists() {
69 progress.add_error(OperationError::new(
70 path.clone(),
71 "File already exists".to_string(),
72 ));
73 let _ = tx
74 .send(CreateResult::Complete(OperationComplete {
75 operation_type: OperationType::CreateFile,
76 succeeded: 0,
77 failed: 1,
78 bytes_processed: 0,
79 errors: progress.errors,
80 }))
81 .await;
82 return;
83 }
84
85 if let Some(parent) = path.parent()
87 && !parent.exists()
88 && let Err(e) = fs::create_dir_all(parent)
89 {
90 progress.add_error(OperationError::new(
91 path.clone(),
92 format!("Failed to create parent directory: {}", e),
93 ));
94 let _ = tx
95 .send(CreateResult::Complete(OperationComplete {
96 operation_type: OperationType::CreateFile,
97 succeeded: 0,
98 failed: 1,
99 bytes_processed: 0,
100 errors: progress.errors,
101 }))
102 .await;
103 return;
104 }
105
106 let path_clone = path.clone();
108 let result = tokio::task::spawn_blocking(move || File::create(&path_clone))
109 .await
110 .map_err(|e| format!("Task failed: {}", e));
111
112 match result {
113 Ok(Ok(_)) => {
114 progress.complete_file(0);
115 let _ = tx
116 .send(CreateResult::Complete(OperationComplete {
117 operation_type: OperationType::CreateFile,
118 succeeded: 1,
119 failed: 0,
120 bytes_processed: 0,
121 errors: vec![],
122 }))
123 .await;
124 }
125 Ok(Err(e)) => {
126 progress.add_error(OperationError::new(
127 path,
128 format!("Failed to create file: {}", e),
129 ));
130 let _ = tx
131 .send(CreateResult::Complete(OperationComplete {
132 operation_type: OperationType::CreateFile,
133 succeeded: 0,
134 failed: 1,
135 bytes_processed: 0,
136 errors: progress.errors,
137 }))
138 .await;
139 }
140 Err(e) => {
141 progress.add_error(OperationError::new(path, e));
142 let _ = tx
143 .send(CreateResult::Complete(OperationComplete {
144 operation_type: OperationType::CreateFile,
145 succeeded: 0,
146 failed: 1,
147 bytes_processed: 0,
148 errors: progress.errors,
149 }))
150 .await;
151 }
152 }
153}
154
155async fn create_directory_impl(path: PathBuf, tx: mpsc::Sender<CreateResult>) {
157 let mut progress = OperationProgress::new(OperationType::CreateDirectory, 1, 0);
158 progress.set_current_file(Some(path.clone()));
159
160 let _ = tx.send(CreateResult::Progress(progress.clone())).await;
161
162 if let Some(name) = path.file_name().and_then(|n| n.to_str())
164 && let Err(e) = validate_filename(name)
165 {
166 progress.add_error(OperationError::new(path.clone(), e));
167 let _ = tx
168 .send(CreateResult::Complete(OperationComplete {
169 operation_type: OperationType::CreateDirectory,
170 succeeded: 0,
171 failed: 1,
172 bytes_processed: 0,
173 errors: progress.errors,
174 }))
175 .await;
176 return;
177 }
178
179 if path.exists() {
181 progress.add_error(OperationError::new(
182 path.clone(),
183 "Directory already exists".to_string(),
184 ));
185 let _ = tx
186 .send(CreateResult::Complete(OperationComplete {
187 operation_type: OperationType::CreateDirectory,
188 succeeded: 0,
189 failed: 1,
190 bytes_processed: 0,
191 errors: progress.errors,
192 }))
193 .await;
194 return;
195 }
196
197 let path_clone = path.clone();
199 let result = tokio::task::spawn_blocking(move || fs::create_dir_all(&path_clone))
200 .await
201 .map_err(|e| format!("Task failed: {}", e));
202
203 match result {
204 Ok(Ok(())) => {
205 progress.complete_file(0);
206 let _ = tx
207 .send(CreateResult::Complete(OperationComplete {
208 operation_type: OperationType::CreateDirectory,
209 succeeded: 1,
210 failed: 0,
211 bytes_processed: 0,
212 errors: vec![],
213 }))
214 .await;
215 }
216 Ok(Err(e)) => {
217 progress.add_error(OperationError::new(
218 path,
219 format!("Failed to create directory: {}", e),
220 ));
221 let _ = tx
222 .send(CreateResult::Complete(OperationComplete {
223 operation_type: OperationType::CreateDirectory,
224 succeeded: 0,
225 failed: 1,
226 bytes_processed: 0,
227 errors: progress.errors,
228 }))
229 .await;
230 }
231 Err(e) => {
232 progress.add_error(OperationError::new(path, e));
233 let _ = tx
234 .send(CreateResult::Complete(OperationComplete {
235 operation_type: OperationType::CreateDirectory,
236 succeeded: 0,
237 failed: 1,
238 bytes_processed: 0,
239 errors: progress.errors,
240 }))
241 .await;
242 }
243 }
244}