Skip to content

Commit

Permalink
Remove anymap
Browse files Browse the repository at this point in the history
`anymap` has some undefined behavior, and appears to no longer be maintained.
This patch replaces anymap with an opaque `EventAttributes` type, which
contains a number of accessor methods to get and set values. Under the
covers, this type is simply a box of a struct of option types. However,
since it's opaque the underlying type could be swapped out with an
alternative type that can be extended to support more attributes without
breaking users.

Closes notify-rs#306
  • Loading branch information
erickt committed May 10, 2021
1 parent 4074112 commit 6669bd4
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 158 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ exclude = [
]

[dependencies]
anymap = "0.12.1"
bitflags = "1.0.4"
crossbeam-channel = "0.5.0"
filetime = "0.2.6"
Expand Down
274 changes: 126 additions & 148 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// LICENSE.ARTISTIC file, and the Creative Commons Zero 1.0 license.
//! The `Event` type and the hierarchical `EventKind` descriptor.
use anymap::{any::CloneAny, Map};
use std::{
fmt,
hash::{Hash, Hasher},
Expand All @@ -12,9 +11,6 @@ use std::{
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// An `AnyMap` convenience type with the needed bounds for events.
pub type AnyMap = Map<dyn CloneAny + Send + Sync>;

/// An event describing open or close operations on files.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand Down Expand Up @@ -360,28 +356,119 @@ pub struct Event {
/// Arbitrary data may be added to this field, without restriction beyond the `Sync` and
/// `Clone` properties. Some data added here is considered for comparing and hashing, but not
/// all: at this writing this is `Tracker`, `Flag`, `Info`, and `Source`.
#[cfg_attr(feature = "serde", serde(default = "Attributes::default"))]
pub attrs: EventAttributes,
}

/// Additional attributes of the event.
#[derive(Clone, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EventAttributes {
#[cfg_attr(feature = "serde", serde(flatten))]
inner: Option<Box<EventAttributesInner>>,
}

#[derive(Clone, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
struct EventAttributesInner {
/// Tracking ID for events that are related.
///
/// For events generated by backends with the `TrackRelated` capability. Those backends _may_
/// emit events that are related to each other, and tag those with an identical "tracking id"
/// or "cookie". The value is normalised to `usize`.
tracker: Option<usize>,

/// Special Notify flag on the event.
flag: Option<Flag>,

/// Additional information on the event.
///
/// For vendor or custom information, it is recommended to use type wrappers to differentiate
/// entries within the `AnyMap` container and avoid conflicts. For interoperability, one of the
/// “well-known” types (or propose a new one) should be used instead. See the list on the wiki:
/// https://github.com/notify-rs/notify/wiki/Well-Known-Event-Attrs
/// This is to be used for all `Other` variants of the event kind hierarchy. The variant
/// indicates that a consumer should look into the `attrs` for an `Info` value; if that value
/// is missing it should be considered a backend bug.
///
/// There is currently no way to serialise or deserialise arbitrary attributes, only well-known
/// ones handled explicitly here are supported. This might change in the future.
#[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,
/// This attribute may also be present for non-`Other` variants of the event kind, if doing so
/// provides useful precision. For example, the `Modify(Metadata(Extended))` kind suggests
/// using this attribute when information about _what_ extended metadata changed is available.
///
/// This should be a short string, and changes may be considered breaking.
info: Option<String>,

/// The source of the event.
///
/// In most cases this should be a short string, identifying the backend unambiguously. In some
/// cases this may be dynamically generated, but should contain a prefix to make it unambiguous
/// between backends.
source: Option<String>,

/// The process ID of the originator of the event.
///
/// This attribute is experimental and, while included in Notify itself, is not considered
/// stable or standard enough to be part of the serde, eq, hash, and debug representations.
#[cfg_attr(feature = "serde", serde(skip_serializing, skip_deserializing))]
process_id: Option<u32>,
}

/// Tracking ID for events that are related.
///
/// For events generated by backends with the `TrackRelated` capability. Those backends _may_ emit
/// events that are related to each other, and tag those with an identical "tracking id" or
/// "cookie". The value is normalised to `usize`.
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize))]
pub struct Tracker(pub usize);
impl EventAttributes {
/// Creates a new `EventAttributes`.
pub fn new() -> Self {
Self {
inner: None,
}
}

/// Retrieves the tracker ID for an event directly, if present.
pub fn tracker(&self) -> Option<usize> {
self.inner.as_ref().and_then(|inner| inner.tracker.clone())
}

/// Retrieves the Notify flag for an event directly, if present.
pub fn flag(&self) -> Option<Flag> {
self.inner.as_ref().and_then(|inner| inner.flag.clone())
}

/// Retrieves the additional info for an event directly, if present.
pub fn info(&self) -> Option<&str> {
self.inner.as_ref().and_then(|inner| inner.info.as_ref().map(|info| &**info))
}

/// Retrieves the source for an event directly, if present.
pub fn source(&self) -> Option<&str> {
self.inner.as_ref().and_then(|inner| inner.source.as_ref().map(|source| &**source))
}

/// The process ID of the originator of the event.
///
/// This attribute is experimental and, while included in Notify itself, is not considered
/// stable or standard enough to be part of the serde, eq, hash, and debug representations.
pub fn process_id(&self) -> Option<u32> {
self.inner.as_ref().and_then(|inner| inner.process_id.clone())
}

/// Sets the tracker.
pub fn set_tracker(&mut self, tracker: usize) {
self.inner_mut().tracker = Some(tracker);
}

/// Sets the Notify flag onto the event.
pub fn set_flag(&mut self, flag: Flag) {
self.inner_mut().flag = Some(flag);
}

/// Sets additional info onto the event.
pub fn set_info(&mut self, info: &str) {
self.inner_mut().info = Some(info.to_string());
}

/// Sets the process id onto the event.
pub fn set_process_id(&mut self, process_id: u32) {
self.inner_mut().process_id = Some(process_id)
}

fn inner_mut(&mut self) -> &mut EventAttributesInner {
self.inner.get_or_insert_with(|| Box::new(Default::default()))
}
}

/// Special Notify flag on the event.
///
Expand Down Expand Up @@ -413,67 +500,35 @@ pub enum Flag {
Rescan,
}

/// Additional information on the event.
///
/// This is to be used for all `Other` variants of the event kind hierarchy. The variant indicates
/// that a consumer should look into the `attrs` for an `Info` value; if that value is missing it
/// should be considered a backend bug.
///
/// This attribute may also be present for non-`Other` variants of the event kind, if doing so
/// provides useful precision. For example, the `Modify(Metadata(Extended))` kind suggests using
/// this attribute when information about _what_ extended metadata changed is available.
///
/// This should be a short string, and changes may be considered breaking.
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize))]
pub struct Info(pub String);

/// The source of the event.
///
/// In most cases this should be a short string, identifying the backend unambiguously. In some
/// cases this may be dynamically generated, but should contain a prefix to make it unambiguous
/// between backends.
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize))]
pub struct Source(pub String);

/// The process ID of the originator of the event.
///
/// This attribute is experimental and, while included in Notify itself, is not considered stable
/// or standard enough to be part of the serde, eq, hash, and debug representations.
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize))]
pub struct ProcessID(pub u32);

// + typeid attr?

impl Event {
/// Retrieves the tracker ID for an event directly, if present.
pub fn tracker(&self) -> Option<usize> {
self.attrs.get::<Tracker>().map(|v| v.0)
self.attrs.tracker()
}

/// Retrieves the Notify flag for an event directly, if present.
pub fn flag(&self) -> Option<Flag> {
self.attrs.get::<Flag>().cloned()
self.attrs.flag()
}

/// Retrieves the additional info for an event directly, if present.
pub fn info(&self) -> Option<&String> {
self.attrs.get::<Info>().map(|v| &v.0)
pub fn info(&self) -> Option<&str> {
self.attrs.info()
}

/// Retrieves the source for an event directly, if present.
pub fn source(&self) -> Option<&String> {
self.attrs.get::<Source>().map(|v| &v.0)
pub fn source(&self) -> Option<&str> {
self.attrs.source()
}

/// Creates a new `Event` given a kind.
pub fn new(kind: EventKind) -> Self {
Self {
kind,
paths: Vec::new(),
attrs: AnyMap::new(),
attrs: EventAttributes::new(),
}
}

Expand All @@ -500,19 +555,25 @@ impl Event {

/// Sets the tracker.
pub fn set_tracker(mut self, tracker: usize) -> Self {
self.attrs.insert(Tracker(tracker));
self.attrs.set_tracker(tracker);
self
}

/// Sets additional info onto the event.
pub fn set_info(mut self, info: &str) -> Self {
self.attrs.insert(Info(info.into()));
self.attrs.set_info(info);
self
}

/// Sets the Notify flag onto the event.
pub fn set_flag(mut self, flag: Flag) -> Self {
self.attrs.insert(flag);
self.attrs.set_flag(flag);
self
}

/// Sets the process id onto the event.
pub fn set_process_id(mut self, process_id: u32) -> Self {
self.attrs.set_process_id(process_id);
self
}
}
Expand All @@ -534,7 +595,7 @@ impl Default for Event {
Self {
kind: EventKind::default(),
paths: Vec::new(),
attrs: AnyMap::new(),
attrs: EventAttributes::new(),
}
}
}
Expand All @@ -561,86 +622,3 @@ impl Hash for Event {
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)
}
}
3 changes: 2 additions & 1 deletion src/fsevent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ fn translate_flags(flags: fse::StreamFlags, precise: bool) -> Vec<Event> {

if flags.contains(fse::StreamFlags::OWN_EVENT) {
for ev in &mut evs {
ev.attrs.insert(ProcessID(std::process::id()));
*ev = std::mem::replace(ev, Event::default())
.set_process_id(std::process::id());
}
}

Expand Down
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@
//! # }
//! ```
// FIXME: `anymap` crate triggers this lint and we cannot do anything here.
#![allow(where_clauses_object_safety)]
#![deny(missing_docs)]

pub use config::{Config, RecursiveMode};
Expand Down
Loading

0 comments on commit 6669bd4

Please sign in to comment.