Skip to content

Commit

Permalink
Support renaming
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbrochart committed Nov 7, 2022
1 parent ae3ed0d commit 39a2771
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 20 deletions.
43 changes: 24 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ create_exception!(
"Internal or filesystem error."
);

const EMPTY_STRING: String = String::new();

// these need to match `watchfiles/main.py::Change`
const CHANGE_ADDED: u8 = 1;
const CHANGE_MODIFIED: u8 = 2;
const CHANGE_DELETED: u8 = 3;
const CHANGE_MOVED: u8 = 4;

#[derive(Debug)]
enum WatcherEnum {
Expand All @@ -37,7 +40,7 @@ enum WatcherEnum {

#[pyclass]
struct RustNotify {
changes: Arc<Mutex<HashSet<(u8, String)>>>,
changes: Arc<Mutex<HashSet<(u8, String, String)>>>,
error: Arc<Mutex<Option<String>>>,
debug: bool,
watcher: WatcherEnum,
Expand Down Expand Up @@ -82,7 +85,7 @@ impl RustNotify {
poll_delay_ms: u64,
recursive: bool,
) -> PyResult<Self> {
let changes: Arc<Mutex<HashSet<(u8, String)>>> = Arc::new(Mutex::new(HashSet::<(u8, String)>::new()));
let changes: Arc<Mutex<HashSet<(u8, String, String)>>> = Arc::new(Mutex::new(HashSet::<(u8, String, String)>::new()));
let error: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));

let changes_clone = changes.clone();
Expand Down Expand Up @@ -110,33 +113,35 @@ impl RustNotify {
| EventKind::Modify(ModifyKind::Any) => {
// these events sometimes happen when creating files and deleting them, hence these checks
let changes = changes_clone.lock().unwrap();
if changes.contains(&(CHANGE_DELETED, path.clone()))
|| changes.contains(&(CHANGE_ADDED, path.clone()))
if changes.contains(&(CHANGE_DELETED, path.clone(), EMPTY_STRING))
|| changes.contains(&(CHANGE_ADDED, path.clone(), EMPTY_STRING))
{
// file was already deleted or file was added in this batch, ignore this event
return;
} else {
CHANGE_MODIFIED
}
}
EventKind::Modify(ModifyKind::Name(RenameMode::From)) => CHANGE_DELETED,
EventKind::Modify(ModifyKind::Name(RenameMode::To)) => CHANGE_ADDED,
// RenameMode::Both duplicates RenameMode::From & RenameMode::To
EventKind::Modify(ModifyKind::Name(RenameMode::Both)) => return,
EventKind::Modify(ModifyKind::Name(_)) => {
// On macOS the modify name event is triggered when a file is renamed,
// but no information about whether it's the src or dst path is available.
// Hence we have to check if the file exists instead.
if Path::new(&path).exists() {
CHANGE_ADDED
} else {
CHANGE_DELETED
}
}
EventKind::Modify(ModifyKind::Name(RenameMode::Both)) => CHANGE_MOVED,
EventKind::Remove(_) => CHANGE_DELETED,
_ => return,
};
changes_clone.lock().unwrap().insert((change, path));
let path2: String;
if change == CHANGE_MOVED {
let path_buf2 = event.paths[1].clone();
path2 = match path_buf2.to_str() {
Some(s) => s.to_string(),
None => {
let msg = format!("Unable to decode path {:?} to string", path_buf2);
*error_clone.lock().unwrap() = Some(msg);
return;
}
};
}
else {
path2 = EMPTY_STRING;
}
changes_clone.lock().unwrap().insert((change, path, path2));
}
}
Err(e) => {
Expand Down
6 changes: 5 additions & 1 deletion watchfiles/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ class Change(IntEnum):
"""A file or directory was modified, can be either a metadata or data change."""
deleted = 3
"""A file or directory was deleted."""
moved = 4
"""A file or directory was moved."""

def raw_str(self) -> str:
if self == Change.added:
return 'added'
elif self == Change.modified:
return 'modified'
elif self == Change.moved:
return 'moved'
else:
return 'deleted'

Expand Down Expand Up @@ -270,7 +274,7 @@ def _prep_changes(
raw_changes: Set[Tuple[int, str]], watch_filter: Optional[Callable[[Change, str], bool]]
) -> Set[FileChange]:
# if we wanted to be really snazzy, we could move this into rust
changes = {(Change(change), path) for change, path in raw_changes}
changes = {(Change(change), path, path2) for change, path, path2 in raw_changes}
if watch_filter:
changes = {c for c in changes if watch_filter(c[0], c[1])}
return changes
Expand Down

0 comments on commit 39a2771

Please sign in to comment.