use std::{
ffi::{
OsStr,
OsString,
},
mem,
os::unix::ffi::OsStrExt,
sync::Weak,
};
use inotify_sys as ffi;
use fd_guard::FdGuard;
use watches::WatchDescriptor;
pub struct Events<'a> {
fd : Weak<FdGuard>,
buffer : &'a [u8],
num_bytes: usize,
pos : usize,
}
impl<'a> Events<'a> {
pub(crate) fn new(fd: Weak<FdGuard>, buffer: &'a [u8], num_bytes: usize)
-> Self
{
Events {
fd : fd,
buffer : buffer,
num_bytes: num_bytes,
pos : 0,
}
}
}
impl<'a> Iterator for Events<'a> {
type Item = Event<&'a OsStr>;
fn next(&mut self) -> Option<Self::Item> {
if self.pos < self.num_bytes {
let (step, event) = Event::from_buffer(self.fd.clone(), &self.buffer[self.pos..]);
self.pos += step;
Some(event)
}
else {
None
}
}
}
#[derive(Clone, Debug)]
pub struct Event<S> {
pub wd: WatchDescriptor,
pub mask: EventMask,
pub cookie: u32,
pub name: Option<S>,
}
impl<'a> Event<&'a OsStr> {
fn new(fd: Weak<FdGuard>, event: &ffi::inotify_event, name: &'a OsStr)
-> Self
{
let mask = EventMask::from_bits(event.mask)
.expect("Failed to convert event mask. This indicates a bug.");
let wd = ::WatchDescriptor {
id: event.wd,
fd,
};
let name = if name == "" {
None
}
else {
Some(name)
};
Event {
wd,
mask,
cookie: event.cookie,
name,
}
}
pub(crate) fn from_buffer(
fd : Weak<FdGuard>,
buffer: &'a [u8],
)
-> (usize, Self)
{
let event_size = mem::size_of::<ffi::inotify_event>();
assert!(buffer.len() >= event_size);
let event = buffer.as_ptr() as *const ffi::inotify_event;
let event = unsafe { &*event };
let bytes_left_in_buffer = buffer.len() - event_size;
assert!(bytes_left_in_buffer >= event.len as usize);
let bytes_consumed = event_size + event.len as usize;
let name = &buffer[event_size..bytes_consumed];
let name = name
.splitn(2, |b| b == &0u8)
.next()
.unwrap();
let event = Event::new(
fd,
event,
OsStr::from_bytes(name),
);
(bytes_consumed, event)
}
pub(crate) fn into_owned(&self) -> EventOwned {
Event {
wd: self.wd.clone(),
mask: self.mask,
cookie: self.cookie,
name: self.name.map(OsStr::to_os_string),
}
}
}
pub type EventOwned = Event<OsString>;
bitflags! {
pub struct EventMask: u32 {
const ACCESS = ffi::IN_ACCESS;
const ATTRIB = ffi::IN_ATTRIB;
const CLOSE_WRITE = ffi::IN_CLOSE_WRITE;
const CLOSE_NOWRITE = ffi::IN_CLOSE_NOWRITE;
const CREATE = ffi::IN_CREATE;
const DELETE = ffi::IN_DELETE;
const DELETE_SELF = ffi::IN_DELETE_SELF;
const MODIFY = ffi::IN_MODIFY;
const MOVE_SELF = ffi::IN_MOVE_SELF;
const MOVED_FROM = ffi::IN_MOVED_FROM;
const MOVED_TO = ffi::IN_MOVED_TO;
const OPEN = ffi::IN_OPEN;
const IGNORED = ffi::IN_IGNORED;
const ISDIR = ffi::IN_ISDIR;
const Q_OVERFLOW = ffi::IN_Q_OVERFLOW;
const UNMOUNT = ffi::IN_UNMOUNT;
}
}
#[cfg(test)]
mod tests {
use std::{
io::prelude::*,
mem,
slice,
sync,
};
use inotify_sys as ffi;
use super::Event;
#[test]
fn from_buffer_should_not_mistake_next_event_for_name_of_previous_event() {
let mut buffer = [0u8; 1024];
let event = ffi::inotify_event {
wd: 0,
mask: 0,
cookie: 0,
len: 0, };
let event = unsafe {
slice::from_raw_parts(
&event as *const _ as *const u8,
mem::size_of_val(&event),
)
};
(&mut buffer[..]).write(event)
.expect("Failed to write into buffer");
buffer[mem::size_of_val(&event)] = 1;
let (_, event) = Event::from_buffer(
sync::Weak::new(),
&buffer,
);
assert_eq!(event.name, None);
}
}