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::{OperationError, OPERATION_CHANNEL_SIZE};
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 if let Err(e) = validate_filename(name) {
53 progress.add_error(OperationError::new(path.clone(), e));
54 let _ = tx
55 .send(CreateResult::Complete(OperationComplete {
56 operation_type: OperationType::CreateFile,
57 succeeded: 0,
58 failed: 1,
59 bytes_processed: 0,
60 errors: progress.errors,
61 }))
62 .await;
63 return;
64 }
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 if !parent.exists() {
88 if let Err(e) = fs::create_dir_all(parent) {
89 progress.add_error(OperationError::new(
90 path.clone(),
91 format!("Failed to create parent directory: {}", e),
92 ));
93 let _ = tx
94 .send(CreateResult::Complete(OperationComplete {
95 operation_type: OperationType::CreateFile,
96 succeeded: 0,
97 failed: 1,
98 bytes_processed: 0,
99 errors: progress.errors,
100 }))
101 .await;
102 return;
103 }
104 }
105 }
106
107 let path_clone = path.clone();
109 let result = tokio::task::spawn_blocking(move || File::create(&path_clone))
110 .await
111 .map_err(|e| format!("Task failed: {}", e));
112
113 match result {
114 Ok(Ok(_)) => {
115 progress.complete_file(0);
116 let _ = tx
117 .send(CreateResult::Complete(OperationComplete {
118 operation_type: OperationType::CreateFile,
119 succeeded: 1,
120 failed: 0,
121 bytes_processed: 0,
122 errors: vec![],
123 }))
124 .await;
125 }
126 Ok(Err(e)) => {
127 progress.add_error(OperationError::new(path, format!("Failed to create file: {}", e)));
128 let _ = tx
129 .send(CreateResult::Complete(OperationComplete {
130 operation_type: OperationType::CreateFile,
131 succeeded: 0,
132 failed: 1,
133 bytes_processed: 0,
134 errors: progress.errors,
135 }))
136 .await;
137 }
138 Err(e) => {
139 progress.add_error(OperationError::new(path, e));
140 let _ = tx
141 .send(CreateResult::Complete(OperationComplete {
142 operation_type: OperationType::CreateFile,
143 succeeded: 0,
144 failed: 1,
145 bytes_processed: 0,
146 errors: progress.errors,
147 }))
148 .await;
149 }
150 }
151}
152
153async fn create_directory_impl(path: PathBuf, tx: mpsc::Sender<CreateResult>) {
155 let mut progress = OperationProgress::new(OperationType::CreateDirectory, 1, 0);
156 progress.set_current_file(Some(path.clone()));
157
158 let _ = tx.send(CreateResult::Progress(progress.clone())).await;
159
160 if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
162 if let Err(e) = validate_filename(name) {
163 progress.add_error(OperationError::new(path.clone(), e));
164 let _ = tx
165 .send(CreateResult::Complete(OperationComplete {
166 operation_type: OperationType::CreateDirectory,
167 succeeded: 0,
168 failed: 1,
169 bytes_processed: 0,
170 errors: progress.errors,
171 }))
172 .await;
173 return;
174 }
175 }
176
177 if path.exists() {
179 progress.add_error(OperationError::new(
180 path.clone(),
181 "Directory already exists".to_string(),
182 ));
183 let _ = tx
184 .send(CreateResult::Complete(OperationComplete {
185 operation_type: OperationType::CreateDirectory,
186 succeeded: 0,
187 failed: 1,
188 bytes_processed: 0,
189 errors: progress.errors,
190 }))
191 .await;
192 return;
193 }
194
195 let path_clone = path.clone();
197 let result = tokio::task::spawn_blocking(move || fs::create_dir_all(&path_clone))
198 .await
199 .map_err(|e| format!("Task failed: {}", e));
200
201 match result {
202 Ok(Ok(())) => {
203 progress.complete_file(0);
204 let _ = tx
205 .send(CreateResult::Complete(OperationComplete {
206 operation_type: OperationType::CreateDirectory,
207 succeeded: 1,
208 failed: 0,
209 bytes_processed: 0,
210 errors: vec![],
211 }))
212 .await;
213 }
214 Ok(Err(e)) => {
215 progress.add_error(OperationError::new(
216 path,
217 format!("Failed to create directory: {}", e),
218 ));
219 let _ = tx
220 .send(CreateResult::Complete(OperationComplete {
221 operation_type: OperationType::CreateDirectory,
222 succeeded: 0,
223 failed: 1,
224 bytes_processed: 0,
225 errors: progress.errors,
226 }))
227 .await;
228 }
229 Err(e) => {
230 progress.add_error(OperationError::new(path, e));
231 let _ = tx
232 .send(CreateResult::Complete(OperationComplete {
233 operation_type: OperationType::CreateDirectory,
234 succeeded: 0,
235 failed: 1,
236 bytes_processed: 0,
237 errors: progress.errors,
238 }))
239 .await;
240 }
241 }
242}