1use crate::error::{Error, ErrorKind, Result};
2use std;
3use std::fs::{remove_file, File};
4use std::io::{Read, Write};
5use std::path::Path;
6
7// Options and flags which can be used to configure how a file will be copied or moved.
8pub struct CopyOptions {
9 /// Sets the option true for overwrite existing files.
10 pub overwrite: bool,
11 /// Sets the option true for skip existing files.
12 pub skip_exist: bool,
13 /// Sets buffer size for copy/move work only with receipt information about process work.
14 pub buffer_size: usize,
15}
16
17impl CopyOptions {
18 /// Initialize struct CopyOptions with default value.
19 ///
20 /// ```rust,ignore
21 ///
22 /// overwrite: false
23 ///
24 /// skip_exist: false
25 ///
26 /// buffer_size: 64000 //64kb
27 /// ```
28 pub fn new() -> CopyOptions {
29 CopyOptions {
30 overwrite: false,
31 skip_exist: false,
32 buffer_size: 64000, //64kb
33 }
34 }
35
36 /// Sets the option true for overwrite existing files.
37 pub fn overwrite(mut self, overwrite: bool) -> Self {
38 self.overwrite = overwrite;
39 self
40 }
41
42 /// Sets the option true for skip existing files.
43 pub fn skip_exist(mut self, skip_exist: bool) -> Self {
44 self.skip_exist = skip_exist;
45 self
46 }
47
48 /// Sets buffer size for copy/move work only with receipt information about process work.
49 pub fn buffer_size(mut self, buffer_size: usize) -> Self {
50 self.buffer_size = buffer_size;
51 self
52 }
53}
54
55impl Default for CopyOptions {
56 fn default() -> Self {
57 CopyOptions::new()
58 }
59}
60
61/// A structure which stores information about the current status of a file that's copied or moved. .
62pub struct TransitProcess {
63 /// Copied bytes on this time.
64 pub copied_bytes: u64,
65 /// All the bytes which should to copy or move.
66 pub total_bytes: u64,
67}
68
69/// Copies the contents of one file to another. This function will also copy the permission
70/// bits of the original file to the destination file.
71///
72/// # Errors
73///
74/// This function will return an error in the following situations, but is not limited to just
75/// these cases:
76///
77/// * This `from` path is not a file.
78/// * This `from` file does not exist.
79/// * The current process does not have the permission to access `from` or write `to`.
80///
81/// # Example
82///
83/// ```rust,ignore
84/// extern crate fs_extra;
85/// use fs_extra::file::copy;
86///
87/// let options = CopyOptions::new(); //Initialize default values for CopyOptions
88/// copy("dir1/foo.txt", "dir2/bar.txt", &options)?; // Copy dir1/foo.txt to dir2/bar.txt
89///
90/// ```
91pub fn copy<P, Q>(from: P, to: Q, options: &CopyOptions) -> Result<u64>
92where
93 P: AsRef<Path>,
94 Q: AsRef<Path>,
95{
96 let from = from.as_ref();
97 if !from.exists() {
98 if let Some(msg) = from.to_str() {
99 let msg = format!("Path \"{}\" does not exist or you don't have access!", msg);
100 err!(&msg, ErrorKind::NotFound);
101 }
102 err!(
103 "Path does not exist or you don't have access!",
104 ErrorKind::NotFound
105 );
106 }
107
108 if !from.is_file() {
109 if let Some(msg) = from.to_str() {
110 let msg = format!("Path \"{}\" is not a file!", msg);
111 err!(&msg, ErrorKind::InvalidFile);
112 }
113 err!("Path is not a file!", ErrorKind::InvalidFile);
114 }
115
116 if !options.overwrite && to.as_ref().exists() {
117 if options.skip_exist {
118 return Ok(0);
119 }
120
121 if let Some(msg) = to.as_ref().to_str() {
122 let msg = format!("Path \"{}\" exists", msg);
123 err!(&msg, ErrorKind::AlreadyExists);
124 }
125 }
126
127 Ok(std::fs::copy(from, to)?)
128}
129
130/// Copies the contents of one file to another file with information about progress.
131/// This function will also copy the permission bits of the original file to the
132/// destination file.
133///
134/// # Errors
135///
136/// This function will return an error in the following situations, but is not limited to just
137/// these cases:
138///
139/// * This `from` path is not a file.
140/// * This `from` file does not exist.
141/// * The current process does not have the permission to access `from` or write `to`.
142///
143/// # Example
144/// ```rust,ignore
145/// extern crate fs_extra;
146/// use fs_extra::file::copy_with_progress;
147///
148/// let options = CopyOptions::new(); //Initialize default values for CopyOptions
149/// let handle = |process_info: TransitProcess| println!("{}", process_info.total_bytes);
150///
151/// // Copy dir1/foo.txt to dir2/foo.txt
152/// copy_with_progress("dir1/foo.txt", "dir2/foo.txt", &options, handle)?;
153///
154/// ```
155pub fn copy_with_progress<P, Q, F>(
156 from: P,
157 to: Q,
158 options: &CopyOptions,
159 mut progress_handler: F,
160) -> Result<u64>
161where
162 P: AsRef<Path>,
163 Q: AsRef<Path>,
164 F: FnMut(TransitProcess),
165{
166 let from = from.as_ref();
167 if !from.exists() {
168 if let Some(msg) = from.to_str() {
169 let msg = format!("Path \"{}\" does not exist or you don't have access!", msg);
170 err!(&msg, ErrorKind::NotFound);
171 }
172 err!(
173 "Path does not exist or you don't have access!",
174 ErrorKind::NotFound
175 );
176 }
177
178 if !from.is_file() {
179 if let Some(msg) = from.to_str() {
180 let msg = format!("Path \"{}\" is not a file!", msg);
181 err!(&msg, ErrorKind::InvalidFile);
182 }
183 err!("Path is not a file!", ErrorKind::InvalidFile);
184 }
185
186 if !options.overwrite && to.as_ref().exists() {
187 if options.skip_exist {
188 return Ok(0);
189 }
190
191 if let Some(msg) = to.as_ref().to_str() {
192 let msg = format!("Path \"{}\" exists", msg);
193 err!(&msg, ErrorKind::AlreadyExists);
194 }
195 }
196 let mut file_from = File::open(from)?;
197 let mut buf = vec![0; options.buffer_size];
198 let file_size = file_from.metadata()?.len();
199 let mut copied_bytes: u64 = 0;
200
201 let mut file_to = File::create(to)?;
202 while !buf.is_empty() {
203 match file_from.read(&mut buf) {
204 Ok(0) => break,
205 Ok(n) => {
206 let written_bytes = file_to.write(&buf[..n])?;
207 if written_bytes != n {
208 err!("Couldn't write the whole buffer to file", ErrorKind::Other);
209 }
210 copied_bytes += n as u64;
211 let data = TransitProcess {
212 copied_bytes,
213 total_bytes: file_size,
214 };
215 progress_handler(data);
216 }
217 Err(ref e) if e.kind() == ::std::io::ErrorKind::Interrupted => {}
218 Err(e) => return Err(::std::convert::From::from(e)),
219 }
220 }
221 Ok(file_size)
222}
223
224/// Moves a file from one place to another. This function will also copy the permission
225/// bits of the original file to the destination file.
226///
227/// # Errors
228///
229/// This function will return an error in the following situations, but is not limited to just
230/// these cases:
231///
232/// * This `from` path is not a file.
233/// * This `from` file does not exist.
234/// * The current process does not have the permission to access `from` or write `to`.
235///
236/// # Example
237/// ```rust,ignore
238/// extern crate fs_extra;
239/// use fs_extra::file::move_file;
240///
241/// let options = CopyOptions::new(); //Initialize default values for CopyOptions
242/// move_file("dir1/foo.txt", "dir2/foo.txt", &options)?; // Move dir1/foo.txt to dir2/foo.txt
243///
244/// ```
245pub fn move_file<P, Q>(from: P, to: Q, options: &CopyOptions) -> Result<u64>
246where
247 P: AsRef<Path>,
248 Q: AsRef<Path>,
249{
250 let mut is_remove: bool = true;
251 if options.skip_exist && to.as_ref().exists() && !options.overwrite {
252 is_remove = false;
253 }
254 let result: u64 = copy(&from, to, options)?;
255 if is_remove {
256 remove(path:from)?;
257 }
258
259 Ok(result)
260}
261
262/// Moves a file from one place to another with information about progress.
263/// This function will also copy the permission bits of the original file to the
264/// destination file.
265///
266/// # Errors
267///
268/// This function will return an error in the following situations, but is not limited to just
269/// these cases:
270///
271/// * This `from` path is not a file.
272/// * This `from` file does not exist.
273/// * The current process does not have the permission to access `from` or write `to`.
274///
275/// # Example
276/// ```rust,ignore
277/// extern crate fs_extra;
278/// use fs_extra::file::move_file;
279///
280/// let options = CopyOptions::new(); //Initialize default values for CopyOptions
281/// let handle = |process_info: TransitProcess| println!("{}", process_info.total_bytes);
282///
283/// // Move dir1/foo.txt to dir2/foo.txt
284/// move_file("dir1/foo.txt", "dir2/foo.txt", &options, handle)?;
285///
286/// ```
287pub fn move_file_with_progress<P, Q, F>(
288 from: P,
289 to: Q,
290 options: &CopyOptions,
291 progress_handler: F,
292) -> Result<u64>
293where
294 P: AsRef<Path>,
295 Q: AsRef<Path>,
296 F: FnMut(TransitProcess),
297{
298 let mut is_remove: bool = true;
299 if options.skip_exist && to.as_ref().exists() && !options.overwrite {
300 is_remove = false;
301 }
302 let result: u64 = copy_with_progress(&from, to, options, progress_handler)?;
303 if is_remove {
304 remove(path:from)?;
305 }
306
307 Ok(result)
308}
309
310/// Removes a file from the filesystem.
311///
312/// # Errors
313///
314/// This function will return an error in the following situations, but is not limited to just
315/// these cases:
316///
317/// * The current process does not have the permission to access `path`.
318///
319/// # Example
320/// ```rust,ignore
321/// extern crate fs_extra;
322/// use fs_extra::file::remove;
323///
324/// remove("foo.txt" )?; // Remove foo.txt
325///
326/// ```
327pub fn remove<P>(path: P) -> Result<()>
328where
329 P: AsRef<Path>,
330{
331 if path.as_ref().exists() {
332 Ok(remove_file(path)?)
333 } else {
334 Ok(())
335 }
336}
337
338/// Read file contents, placing them into `String`.
339///
340/// # Errors
341///
342/// This function will return an error in the following situations, but is not limited to just
343/// these cases:
344///
345/// * This `path` is not a file.
346/// * This `path` file does not exist.
347/// * The current process does not have the permission to access `path`.
348///
349/// # Example
350/// ```rust,ignore
351/// extern crate fs_extra;
352/// use fs_extra::file::read_to_string;
353///
354/// let file_content = read_to_string("foo.txt" )?; // Get file content from foo.txt
355/// println!("{}", file_content);
356///
357/// ```
358pub fn read_to_string<P>(path: P) -> Result<String>
359where
360 P: AsRef<Path>,
361{
362 let path: &Path = path.as_ref();
363 if path.exists() && !path.is_file() {
364 if let Some(msg: &str) = path.to_str() {
365 let msg: String = format!("Path \"{}\" is not a file!", msg);
366 err!(&msg, ErrorKind::InvalidFile);
367 }
368 err!("Path is not a file!", ErrorKind::InvalidFile);
369 }
370
371 let mut file: File = File::open(path)?;
372 let mut result: String = String::new();
373 file.read_to_string(&mut result)?;
374
375 Ok(result)
376}
377
378/// Write `String` content into file.
379///
380/// # Errors
381///
382/// This function will return an error in the following situations, but is not limited to just
383/// these cases:
384///
385/// * This `path` is not a file.
386/// * This `path` file does not exist.
387/// * The current process does not have the permission to access `path`.
388///
389/// # Example
390/// ```rust,ignore
391/// extern crate fs_extra;
392/// use fs_extra::file::read_to_string;
393///
394/// write_all("foo.txt", "contents" )?; // Create file foo.txt and send content inside
395///
396/// ```
397pub fn write_all<P>(path: P, content: &str) -> Result<()>
398where
399 P: AsRef<Path>,
400{
401 let path: &Path = path.as_ref();
402 if path.exists() && !path.is_file() {
403 if let Some(msg: &str) = path.to_str() {
404 let msg: String = format!("Path \"{}\" is not a file!", msg);
405 err!(&msg, ErrorKind::InvalidFile);
406 }
407 err!("Path is not a file!", ErrorKind::InvalidFile);
408 }
409
410 let mut f: File = File::create(path)?;
411
412 Ok(f.write_all(buf:content.as_bytes())?)
413}
414