Skip to content

Commit

Permalink
Refined windows implementation.
Browse files Browse the repository at this point in the history
Added error kind `RestoreTwins`.
  • Loading branch information
ArturKovacs committed Nov 25, 2019
1 parent 743b2f3 commit d105553
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 150 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ scopeguard = "1.0.0"
git = "https://github.com/ArturKovacs/winapi-rs.git"
rev = "fe297b9830c804b274e2206d3aebfe9dc33e39cb"
features = [
"combaseapi", "objbase", "shlobj", "shellapi", "winerror", "shlobj", "shlwapi", "shobjidl_core",
"combaseapi", "objbase", "objidl", "shlobj", "shellapi", "winerror", "shlobj", "shlwapi", "shobjidl_core",
"shobjidl", "oleauto", "oaidl", "wtypes", "errhandlingapi", "timezoneapi", "winuser"
]
39 changes: 39 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::HashSet;
use std::ffi::OsString;
use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -50,6 +51,9 @@ impl Error {
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
pub fn into_kind(self) -> ErrorKind {
self.kind
}
pub fn into_source(self) -> Option<Box<dyn std::error::Error + 'static>> {
self.source
}
Expand Down Expand Up @@ -129,10 +133,22 @@ pub enum ErrorKind {
/// restoration of the files is halted meaning that there may be files that could be restored
/// but were left in the trash due to the error.
///
/// One should not assume any relationship between the order that the items were supplied and
/// the list of remaining items. That is to say, it may be that the item that collided was in
/// the middle of the provided list but the remaining items' list contains all the provided
/// items.
///
/// `path`: The path of the file that's blocking the trash item from being restored.
/// `remaining_items`: All items that were not restored in the order they were provided,
/// starting with the item that triggered the error.
RestoreCollision { path: PathBuf, remaining_items: Vec<TrashItem> },

/// This sort of error is returned when multiple items with the same `original_path` were
/// requested to be restored. These items are referred to as twins here.
///
/// `path`: The `original_path` of the twins.
/// `items`: The complete list of items that were handed over to the `restore_all` function.
RestoreTwins { path: PathBuf, items: Vec<TrashItem> },
}

/// This struct holds information about a single item within the trash.
Expand Down Expand Up @@ -218,6 +234,29 @@ pub fn restore_all<I>(items: I) -> Result<(), Error>
where
I: IntoIterator<Item = TrashItem>,
{
// Check for twins here cause that's pretty platform independent.
struct ItemWrapper<'a>(&'a TrashItem);
impl<'a> PartialEq for ItemWrapper<'a> {
fn eq(&self, other: &Self) -> bool {
self.0.original_path() == other.0.original_path()
}
}
impl<'a> Eq for ItemWrapper<'a> {}
impl<'a> Hash for ItemWrapper<'a> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.original_path().hash(state);
}
}
let items = items.into_iter().collect::<Vec<_>>();
let mut item_set = HashSet::with_capacity(items.len());
for item in items.iter() {
if !item_set.insert(ItemWrapper(item)) {
return Err(Error::kind_only(ErrorKind::RestoreTwins {
path: item.original_path(),
items: items,
}));
}
}
platform::restore_all(items)
}

Expand Down
65 changes: 55 additions & 10 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,15 +190,23 @@ fn restore_collision() {
.collect();
targets.sort_by(|a, b| a.name.cmp(&b.name));
assert_eq!(targets.len(), file_count);
let mut remaining_count = 0;
let remaining_count;
match trash::restore_all(targets) {
Err(e) => match e.kind() {
trash::ErrorKind::RestoreCollision { remaining_items, .. } => {
let iter = names.iter().skip(file_count - collision_remaining).zip(remaining_items);
for (original, remaining) in iter {
assert_eq!(original, &remaining.name);
remaining_count += 1;
let contains = |v: &Vec<trash::TrashItem>, name: &String| {
for curr in v.iter() {
if *curr.name == *name {
return true;
}
}
false
};
// Are all items that got restored, reside in the folder?
for path in names.iter().filter(|filename| !contains(&remaining_items, filename)) {
assert!(File::open(path).is_ok());
}
remaining_count = remaining_items.len();
}
_ => panic!("{:?}", e),
},
Expand All @@ -212,12 +220,49 @@ fn restore_collision() {
.filter(|x| x.name.starts_with(&file_name_prefix))
.collect::<Vec<_>>();
assert_eq!(remaining.len(), remaining_count);
// They are not in the trash anymore but they should be at their original location
for path in names.iter().take(file_count - collision_remaining) {
assert!(File::open(path).is_ok());
}
trash::purge_all(remaining).unwrap();
for path in names.iter() {
std::fs::remove_file(path).unwrap();
// This will obviously fail on the items that both didn't collide and weren't restored.
let _ = std::fs::remove_file(path);
}
}

#[test]
fn restore_twins() {
let file_name_prefix = get_unique_name();
let file_count: usize = 4;
let names: Vec<_> = (0..file_count).map(|i| format!("{}#{}", file_name_prefix, i)).collect();
for path in names.iter() {
File::create(path).unwrap();
}
trash::remove_all(&names).unwrap();

let twin_name = &names[1];
File::create(twin_name).unwrap();
trash::remove(&twin_name).unwrap();

let mut targets: Vec<_> = trash::list()
.unwrap()
.into_iter()
.filter(|x| x.name.starts_with(&file_name_prefix))
.collect();
targets.sort_by(|a, b| a.name.cmp(&b.name));
assert_eq!(targets.len(), file_count + 1); // plus one for one of the twins
match trash::restore_all(targets) {
Err(e) => match e.kind() {
trash::ErrorKind::RestoreTwins { path, .. } => {
assert_eq!(path.file_name().unwrap().to_str().unwrap(), twin_name);
match e.into_kind() {
trash::ErrorKind::RestoreTwins { items, .. } => {
trash::purge_all(items).unwrap();
}
_ => unreachable!(),
}
}
_ => panic!("{:?}", e),
},
Ok(()) => panic!(
"restore_all was expected to return `trash::ErrorKind::RestoreTwins` but did not."
),
}
}

0 comments on commit d105553

Please sign in to comment.