use anymap::{any::CloneAny, Map};
use std::{
fmt,
hash::{Hash, Hasher},
path::PathBuf,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub type AnyMap = Map<dyn CloneAny + Send + Sync>;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum AccessMode {
Any,
Execute,
Read,
Write,
Other,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "kind", content = "mode"))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum AccessKind {
Any,
Read,
Open(AccessMode),
Close(AccessMode),
Other,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "kind"))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum CreateKind {
Any,
File,
Folder,
Other,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum DataChange {
Any,
Size,
Content,
Other,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum MetadataKind {
Any,
AccessTime,
WriteTime,
Permissions,
Ownership,
Extended,
Other,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum RenameMode {
Any,
To,
From,
Both,
Other,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "kind", content = "mode"))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum ModifyKind {
Any,
Data(DataChange),
Metadata(MetadataKind),
#[cfg_attr(feature = "serde", serde(rename = "rename"))]
Name(RenameMode),
Other,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "kind"))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum RemoveKind {
Any,
File,
Folder,
Other,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum EventKind {
Any,
Access(AccessKind),
Create(CreateKind),
Modify(ModifyKind),
Remove(RemoveKind),
Other,
}
impl EventKind {
pub fn is_access(&self) -> bool {
match *self {
EventKind::Access(_) => true,
_ => false,
}
}
pub fn is_create(&self) -> bool {
match *self {
EventKind::Create(_) => true,
_ => false,
}
}
pub fn is_modify(&self) -> bool {
match *self {
EventKind::Modify(_) => true,
_ => false,
}
}
pub fn is_remove(&self) -> bool {
match *self {
EventKind::Remove(_) => true,
_ => false,
}
}
pub fn is_other(&self) -> bool {
match *self {
EventKind::Other => true,
_ => false,
}
}
}
impl Default for EventKind {
fn default() -> Self {
EventKind::Any
}
}
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Event {
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub kind: EventKind,
pub paths: Vec<PathBuf>,
#[cfg_attr(feature = "serde", serde(default = "AnyMap::new"))]
#[cfg_attr(feature = "serde", serde(deserialize_with = "attr_serde::deserialize"))]
#[cfg_attr(feature = "serde", serde(serialize_with = "attr_serde::serialize"))]
pub attrs: AnyMap,
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize))]
pub struct Tracker(pub usize);
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Flag {
Rescan,
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize))]
pub struct Info(pub String);
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize))]
pub struct Source(pub String);
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize))]
pub struct ProcessID(pub u32);
impl Event {
pub fn tracker(&self) -> Option<usize> {
self.attrs.get::<Tracker>().map(|v| v.0)
}
pub fn flag(&self) -> Option<Flag> {
self.attrs.get::<Flag>().cloned()
}
pub fn info(&self) -> Option<&String> {
self.attrs.get::<Info>().map(|v| &v.0)
}
pub fn source(&self) -> Option<&String> {
self.attrs.get::<Source>().map(|v| &v.0)
}
pub fn new(kind: EventKind) -> Self {
Self {
kind,
paths: Vec::new(),
attrs: AnyMap::new(),
}
}
pub fn set_kind(mut self, kind: EventKind) -> Self {
self.kind = kind;
self
}
pub fn add_path(mut self, path: PathBuf) -> Self {
self.paths.push(path);
self
}
pub fn add_some_path(self, path: Option<PathBuf>) -> Self {
if let Some(path) = path {
self.add_path(path)
} else {
self
}
}
pub fn set_tracker(mut self, tracker: usize) -> Self {
self.attrs.insert(Tracker(tracker));
self
}
pub fn set_info(mut self, info: &str) -> Self {
self.attrs.insert(Info(info.into()));
self
}
pub fn set_flag(mut self, flag: Flag) -> Self {
self.attrs.insert(flag);
self
}
}
impl fmt::Debug for Event {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Event")
.field("kind", &self.kind)
.field("paths", &self.paths)
.field("attr:tracker", &self.tracker())
.field("attr:flag", &self.flag())
.field("attr:info", &self.info())
.field("attr:source", &self.source())
.finish()
}
}
impl Default for Event {
fn default() -> Self {
Self {
kind: EventKind::default(),
paths: Vec::new(),
attrs: AnyMap::new(),
}
}
}
impl Eq for Event {}
impl PartialEq for Event {
fn eq(&self, other: &Self) -> bool {
self.kind.eq(&other.kind)
&& self.paths.eq(&other.paths)
&& self.tracker().eq(&other.tracker())
&& self.flag().eq(&other.flag())
&& self.info().eq(&other.info())
&& self.source().eq(&other.source())
}
}
impl Hash for Event {
fn hash<H: Hasher>(&self, state: &mut H) {
self.kind.hash(state);
self.paths.hash(state);
self.tracker().hash(state);
self.flag().hash(state);
self.info().hash(state);
self.source().hash(state);
}
}
#[cfg(feature = "serde")]
mod attr_serde {
use super::*;
use serde::{
de::Deserializer,
ser::{SerializeMap, Serializer},
};
use std::collections::HashMap;
pub(crate) fn serialize<S>(attrs: &AnyMap, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let tracker = attrs.get::<Tracker>().map(|v| v.0.clone());
let flag = attrs.get::<Flag>().map(|v| v.clone());
let info = attrs.get::<Info>().map(|v| v.0.clone());
let source = attrs.get::<Source>().map(|v| v.0.clone());
let mut length = 0;
if tracker.is_some() {
length += 1;
}
if flag.is_some() {
length += 1;
}
if info.is_some() {
length += 1;
}
if source.is_some() {
length += 1;
}
let mut map = s.serialize_map(Some(length))?;
if let Some(val) = tracker {
map.serialize_entry("tracker", &val)?;
}
if let Some(val) = flag {
map.serialize_entry("flag", &val)?;
}
if let Some(val) = info {
map.serialize_entry("info", &val)?;
}
if let Some(val) = source {
map.serialize_entry("source", &val)?;
}
map.end()
}
pub(crate) fn deserialize<'de, D>(d: D) -> Result<AnyMap, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Clone, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[serde(untagged)]
enum Attr {
Tracker(Option<Tracker>),
Flag(Option<Flag>),
Info(Option<Info>),
Source(Option<Source>),
}
#[derive(Deserialize)]
struct Attrs(pub HashMap<String, Attr>);
let attrs = Attrs::deserialize(d)?;
let mut map = AnyMap::with_capacity(attrs.0.len());
if let Some(Attr::Tracker(Some(val))) = attrs.0.get("tracker").cloned() {
map.insert(val);
}
if let Some(Attr::Flag(Some(val))) = attrs.0.get("flag").cloned() {
map.insert(val);
}
if let Some(Attr::Info(Some(val))) = attrs.0.get("info").cloned() {
map.insert(val);
}
if let Some(Attr::Source(Some(val))) = attrs.0.get("source").cloned() {
map.insert(val);
}
Ok(map)
}
}