fs_extra/
file.rs

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 = true;
251    if options.skip_exist && to.as_ref().exists() && !options.overwrite {
252        is_remove = false;
253    }
254    let result = copy(&from, to, options)?;
255    if is_remove {
256        remove(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 = true;
299    if options.skip_exist && to.as_ref().exists() && !options.overwrite {
300        is_remove = false;
301    }
302    let result = copy_with_progress(&from, to, options, progress_handler)?;
303    if is_remove {
304        remove(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.as_ref();
363    if path.exists() && !path.is_file() {
364        if let Some(msg) = path.to_str() {
365            let msg = 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::open(path)?;
372    let mut result = 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.as_ref();
402    if path.exists() && !path.is_file() {
403        if let Some(msg) = path.to_str() {
404            let msg = 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::create(path)?;
411
412    Ok(f.write_all(content.as_bytes())?)
413}