Skip to content

Commit d24dc7d

Browse files
committed
Implement mtime and atime copying
1 parent 752fc4d commit d24dc7d

File tree

7 files changed

+241
-75
lines changed

7 files changed

+241
-75
lines changed

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ language: rust
22
rust:
33
- stable
44
script:
5-
- cargo build --verbose
6-
- cargo test --verbose
7-
- cargo doc
5+
- cargo build --verbose --features lifetime
6+
- cargo test --verbose --features filetime
7+
- cargo doc --features lifetime

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ include = [
1818
]
1919

2020
[dependencies]
21+
filetime = { version = "0.2", optional = true }

src/dir.rs

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use std::fs::{create_dir, create_dir_all, read_dir, remove_dir_all, Metadata};
44
use std::path::{Path, PathBuf};
55
use std::time::SystemTime;
66

7+
#[cfg(feature = "filetime")]
8+
use crate::time::{TimeOptions, copy_time};
9+
710
/// Options and flags which can be used to configure how a file will be copied or moved.
811
#[derive(Clone)]
912
pub struct CopyOptions {
@@ -21,6 +24,9 @@ pub struct CopyOptions {
2124
///
2225
/// Warning: Work only for copy operations!
2326
pub depth: u64,
27+
/// File time options.
28+
#[cfg(feature = "filetime")]
29+
pub time_options: TimeOptions,
2430
}
2531

2632
impl CopyOptions {
@@ -43,6 +49,8 @@ impl CopyOptions {
4349
copy_inside: false,
4450
content_only: false,
4551
depth: 0,
52+
#[cfg(feature = "filetime")]
53+
time_options: TimeOptions::new(),
4654
}
4755
}
4856
}
@@ -561,17 +569,9 @@ where
561569
}
562570

563571
let dir_content = get_dir_content2(from, &read_options)?;
564-
for directory in dir_content.directories {
565-
let tmp_to = Path::new(&directory).strip_prefix(from)?;
566-
let dir = to.join(&tmp_to);
567-
if !dir.exists() {
568-
if options.copy_inside {
569-
create_all(dir, false)?;
570-
} else {
571-
create(dir, false)?;
572-
}
573-
}
574-
}
572+
573+
copy_dir_structure(from, to.as_path(), &dir_content, &options);
574+
575575
let mut result: u64 = 0;
576576
for file in dir_content.files {
577577
let to = to.to_path_buf();
@@ -582,6 +582,8 @@ where
582582
overwrite: options.overwrite,
583583
skip_exist: options.skip_exist,
584584
buffer_size: options.buffer_size,
585+
#[cfg(feature = "filetime")]
586+
time_options: options.time_options.clone(),
585587
};
586588
let mut result_copy: Result<u64>;
587589
let mut work = true;
@@ -841,17 +843,8 @@ where
841843
}
842844

843845
let dir_content = get_dir_content2(from, &read_options)?;
844-
for directory in dir_content.directories {
845-
let tmp_to = Path::new(&directory).strip_prefix(from)?;
846-
let dir = to.join(&tmp_to);
847-
if !dir.exists() {
848-
if options.copy_inside {
849-
create_all(dir, false)?;
850-
} else {
851-
create(dir, false)?;
852-
}
853-
}
854-
}
846+
847+
copy_dir_structure(from, to.as_path(), &dir_content, &options);
855848

856849
let mut result: u64 = 0;
857850
let mut info_process = TransitProcess {
@@ -880,6 +873,8 @@ where
880873
overwrite: options.overwrite,
881874
skip_exist: options.skip_exist,
882875
buffer_size: options.buffer_size,
876+
#[cfg(feature = "filetime")]
877+
time_options: options.time_options.clone(),
883878
};
884879

885880
if let Some(file_name) = file_name.to_str() {
@@ -1054,17 +1049,9 @@ where
10541049
to.push(dir_name);
10551050
}
10561051
let dir_content = get_dir_content(from)?;
1057-
for directory in dir_content.directories {
1058-
let tmp_to = Path::new(&directory).strip_prefix(from)?;
1059-
let dir = to.join(&tmp_to);
1060-
if !dir.exists() {
1061-
if options.copy_inside {
1062-
create_all(dir, false)?;
1063-
} else {
1064-
create(dir, false)?;
1065-
}
1066-
}
1067-
}
1052+
1053+
copy_dir_structure(from, to.as_path(), &dir_content, &options);
1054+
10681055
let mut result: u64 = 0;
10691056
for file in dir_content.files {
10701057
let to = to.to_path_buf();
@@ -1075,6 +1062,8 @@ where
10751062
overwrite: options.overwrite,
10761063
skip_exist: options.skip_exist,
10771064
buffer_size: options.buffer_size,
1065+
#[cfg(feature = "filetime")]
1066+
time_options: options.time_options.clone(),
10781067
};
10791068

10801069
let mut result_copy: Result<u64>;
@@ -1178,17 +1167,8 @@ where
11781167
}
11791168

11801169
let dir_content = get_dir_content(from)?;
1181-
for directory in dir_content.directories {
1182-
let tmp_to = Path::new(&directory).strip_prefix(from)?;
1183-
let dir = to.join(&tmp_to);
1184-
if !dir.exists() {
1185-
if options.copy_inside {
1186-
create_all(dir, false)?;
1187-
} else {
1188-
create(dir, false)?;
1189-
}
1190-
}
1191-
}
1170+
1171+
copy_dir_structure(from, to.as_path(), &dir_content, &options);
11921172

11931173
let mut result: u64 = 0;
11941174
let mut info_process = TransitProcess {
@@ -1217,6 +1197,8 @@ where
12171197
overwrite: options.overwrite,
12181198
skip_exist: options.skip_exist,
12191199
buffer_size: options.buffer_size,
1200+
#[cfg(feature = "filetime")]
1201+
time_options: options.time_options.clone(),
12201202
};
12211203

12221204
if let Some(file_name) = file_name.to_str() {
@@ -1345,3 +1327,21 @@ pub fn remove<P: AsRef<Path>>(path: P) -> Result<()> {
13451327
Ok(())
13461328
}
13471329
}
1330+
1331+
fn copy_dir_structure(from: &Path, to: &Path, dir_content: &DirContent, options: &CopyOptions) -> Result<()> {
1332+
for directory in &dir_content.directories {
1333+
let path_from = Path::new(directory);
1334+
let tmp_to = path_from.strip_prefix(from)?;
1335+
let dir = to.join(&tmp_to);
1336+
if !dir.exists() {
1337+
if options.copy_inside {
1338+
create_all(dir.as_path(), false)?;
1339+
} else {
1340+
create(dir.as_path(), false)?;
1341+
}
1342+
#[cfg(feature = "filetime")]
1343+
copy_time(&path_from, dir, &options.time_options);
1344+
}
1345+
}
1346+
Ok(())
1347+
}

src/file.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use std::fs::{remove_file, File};
44
use std::io::{Read, Write};
55
use std::path::Path;
66

7+
#[cfg(feature = "filetime")]
8+
use crate::time::{TimeOptions, copy_time};
9+
710
// Options and flags which can be used to configure how a file will be copied or moved.
811
pub struct CopyOptions {
912
/// Sets the option true for overwrite existing files.
@@ -12,6 +15,9 @@ pub struct CopyOptions {
1215
pub skip_exist: bool,
1316
/// Sets buffer size for copy/move work only with receipt information about process work.
1417
pub buffer_size: usize,
18+
/// File time options.
19+
#[cfg(feature = "filetime")]
20+
pub time_options: TimeOptions,
1521
}
1622

1723
impl CopyOptions {
@@ -30,6 +36,8 @@ impl CopyOptions {
3036
overwrite: false,
3137
skip_exist: false,
3238
buffer_size: 64000, //64kb
39+
#[cfg(feature = "filetime")]
40+
time_options: TimeOptions::new(),
3341
}
3442
}
3543
}
@@ -106,7 +114,11 @@ where
106114
}
107115
}
108116

109-
Ok(std::fs::copy(from, to)?)
117+
let result = std::fs::copy(from, to.as_ref())?;
118+
#[cfg(feature = "filetime")]
119+
copy_time(from, to, &options.time_options);
120+
121+
Ok(result)
110122
}
111123

112124
/// Copies the contents of one file to another file with information about progress.
@@ -180,7 +192,7 @@ where
180192
let file_size = file_from.metadata()?.len();
181193
let mut copied_bytes: u64 = 0;
182194

183-
let mut file_to = File::create(to)?;
195+
let mut file_to = File::create(to.as_ref())?;
184196
while !buf.is_empty() {
185197
match file_from.read(&mut buf) {
186198
Ok(0) => break,
@@ -200,6 +212,8 @@ where
200212
Err(e) => return Err(::std::convert::From::from(e)),
201213
}
202214
}
215+
#[cfg(feature = "filetime")]
216+
copy_time(from, to, &options.time_options);
203217
Ok(file_size)
204218
}
205219

src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ macro_rules! err {
1010

1111
/// The error type for fs_extra operations on files and directories.
1212
pub mod error;
13+
14+
/// This module includes an additional method for working with file and
15+
/// directory modification times and access times.
16+
#[cfg(feature = "filetime")]
17+
pub mod time;
18+
1319
/// This module includes additional methods for working with files.
1420
///
1521
/// One of the distinguishing features is receipt information
@@ -354,6 +360,8 @@ where
354360
overwrite: options.overwrite,
355361
skip_exist: options.skip_exist,
356362
buffer_size: options.buffer_size,
363+
#[cfg(feature = "filetime")]
364+
time_options: options.time_options.clone(),
357365
};
358366

359367
if let Some(file_name) = item.file_name() {
@@ -541,6 +549,8 @@ where
541549
overwrite: options.overwrite,
542550
skip_exist: options.skip_exist,
543551
buffer_size: options.buffer_size,
552+
#[cfg(feature = "filetime")]
553+
time_options: options.time_options.clone(),
544554
};
545555

546556
if let Some(file_name) = item.file_name() {
@@ -666,6 +676,8 @@ where
666676
overwrite: options.overwrite,
667677
skip_exist: options.skip_exist,
668678
buffer_size: options.buffer_size,
679+
#[cfg(feature = "filetime")]
680+
time_options: options.time_options.clone(),
669681
};
670682

671683
if let Some(file_name) = item.file_name() {

src/time.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use crate::error::*;
2+
use std::path::Path;
3+
4+
use filetime::{set_file_atime, set_file_mtime, FileTime};
5+
6+
/// Flags which can be used to configure how file and directory times will be
7+
/// assigned.
8+
#[derive(Clone)]
9+
pub struct TimeOptions {
10+
/// Keep the same modification time.
11+
pub retain_modification_time: bool,
12+
/// Keep the same access time.
13+
pub retain_access_time: bool,
14+
}
15+
16+
impl TimeOptions {
17+
/// Initialize struct TimeOptions with default value.
18+
pub fn new() -> Self {
19+
TimeOptions {
20+
retain_modification_time: false,
21+
retain_access_time: false,
22+
}
23+
}
24+
}
25+
26+
/// Assign time attributes for `to` same as in `from`.
27+
pub fn copy_time<P, Q>(from: P, to: Q, options: &TimeOptions) -> Result<()>
28+
where
29+
P: AsRef<Path>,
30+
Q: AsRef<Path>,
31+
{
32+
if options.retain_modification_time || options.retain_access_time {
33+
match from.as_ref().metadata() {
34+
Ok(metadata) => {
35+
let mtime = FileTime::from_last_modification_time(&metadata);
36+
let atime = FileTime::from_last_access_time(&metadata);
37+
if options.retain_modification_time {
38+
set_file_mtime(to.as_ref(), mtime);
39+
}
40+
if options.retain_access_time {
41+
set_file_atime(to.as_ref(), atime);
42+
}
43+
}
44+
Err(_) => {
45+
err!("Could not read metadata", ErrorKind::Other);
46+
}
47+
}
48+
}
49+
Ok(())
50+
}

0 commit comments

Comments
 (0)