0.10.0: move Clone functionality into a feature. 0.10.0
authorChris Morgan <me@chrismorgan.info>
committerChris Morgan <me@chrismorgan.info>
No more separate Git branch for it; Cargo features fit the bill well.
.travis.yml
Cargo.toml
README.md
src/lib.rs
src/raw.rs
src/unchecked_any.rs
src/with_clone.rs [new file with mode: 0644]

index f95fbc00ac10467a2748191c354d96fa419e9d35..42f2b6348cbc2909ee1d2b6f9adb406e749750af 100644 (file)
@@ -3,6 +3,8 @@ env:
   global:
     - secure: nR+DJRUQ9v03nNZMpMu1tGKLKBAqdQsTIAr8ffdl+DUEh3b2jvQ+vLLNFLPjsloqhoOXo7cWO7qVpiE4ZOq2lNDURQjdiZGFjh/Y5+xKy2BqFdV7qQ1JoBzsMyx28tQTYz0mtBsACiCYKKb+ddNX5hpwrsjp8cS7htZktA5kbiU=
 script:
+  - cargo build --verbose --features clone
+  - cargo test --verbose --features clone
   - cargo build --verbose
   - cargo test --verbose
   - cargo doc --verbose
index 7fc8f575901dc1109170b024e5eb132802cd38b2..d1e54482b466baa623b12339378afaa0d04235a7 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "anymap"
-version = "0.9.13"
+version = "0.10.0"
 authors = ["Chris Morgan <me@chrismorgan.info>"]
 description = "A safe and convenient store for one value of each type"
 #documentation = "http://www.rust-ci.org/chris-morgan/anymap/doc/anymap/index.html"
@@ -9,3 +9,6 @@ repository = "https://github.com/chris-morgan/anymap"
 readme = "README.md"
 keywords = ["container", "data-structure", "map"]
 license = "MIT/Apache-2.0"
+
+[features]
+clone = []
index e76bfab894fffa6162e68264fda3caf951a7700f..da5510e2c1c6ceccfb754c2b75d0ae0152ac72b9 100644 (file)
--- a/README.md
+++ b/README.md
@@ -9,23 +9,16 @@ As another example of such an interface, JavaScript objects are exactly the same
 
 Fortunately, we can do better than these things in Rust. Our type system is quite equal to easy, robust expression of such problems.
 
-The ``AnyMap`` type is a friendly wrapper around a ``HashMap<TypeId, Box<Any + 'static>>``, exposing a nice, easy typed interface, perfectly safe and absolutely robust.
+The ``AnyMap`` type is a friendly wrapper around a ``HashMap<TypeId, Box<Any>>``, exposing a nice, easy typed interface, perfectly safe and absolutely robust.
 
 What this means is that in an ``AnyMap`` you may store zero or one values for every type.
 
 Instructions
 ------------
 
-Cargo all the way.
+Cargo all the way: it is `anymap` on crates.io.
 
-The documentation, with examples, [is also available online](http://www.rust-ci.org/chris-morgan/anymap/doc/anymap/struct.AnyMap.html).
-
-Future work
------------
-
-I think that the only thing left for this is filling out additional methods from ``HashMap`` as appropriate.
-
-It’s a very simple thing. (The initial implementation time was under ten minutes.)
+There is an optional `clone` feature on the `anymap` crate; if enabled, your `AnyMap` will require contained types to implement `Clone` and will itself satisfy `Clone`.
 
 Author
 ------
index acc0265a0936107ee6fc411549c5799e1bc44af8..e38670b71c27947e85c9e3d2b582d303435f7ca2 100644 (file)
@@ -7,10 +7,10 @@
 #[cfg(test)]
 extern crate test;
 
-use std::any::{Any, TypeId};
+use std::any::TypeId;
 use std::marker::PhantomData;
 
-use raw::RawAnyMap;
+use raw::{RawAnyMap, Any};
 use unchecked_any::UncheckedAnyExt;
 
 macro_rules! impl_common_methods {
@@ -85,6 +85,8 @@ macro_rules! impl_common_methods {
 
 mod unchecked_any;
 pub mod raw;
+#[cfg(feature = "clone")]
+mod with_clone;
 
 /// A collection containing zero or one values for any given type and allowing convenient,
 /// type-safe access to those values.
@@ -98,7 +100,7 @@ pub mod raw;
 /// data.remove::<i32>();
 /// assert_eq!(data.get::<i32>(), None);
 ///
-/// #[derive(PartialEq, Debug)]
+/// #[derive(Clone, PartialEq, Debug)]
 /// struct Foo {
 ///     str: String,
 /// }
@@ -112,6 +114,7 @@ pub mod raw;
 ///
 /// Values containing non-static references are not permitted.
 #[derive(Debug)]
+#[cfg_attr(feature = "clone", derive(Clone))]
 pub struct AnyMap {
     raw: RawAnyMap,
 }
@@ -300,67 +303,103 @@ fn bench_get_present(b: &mut ::test::Bencher) {
     })
 }
 
-#[test]
-fn test_entry() {
-    #[derive(Debug, PartialEq)] struct A(i32);
-    #[derive(Debug, PartialEq)] struct B(i32);
-    #[derive(Debug, PartialEq)] struct C(i32);
-    #[derive(Debug, PartialEq)] struct D(i32);
-    #[derive(Debug, PartialEq)] struct E(i32);
-    #[derive(Debug, PartialEq)] struct F(i32);
-    #[derive(Debug, PartialEq)] struct J(i32);
-
-    let mut map: AnyMap = AnyMap::new();
-    assert_eq!(map.insert(A(10)), None);
-    assert_eq!(map.insert(B(20)), None);
-    assert_eq!(map.insert(C(30)), None);
-    assert_eq!(map.insert(D(40)), None);
-    assert_eq!(map.insert(E(50)), None);
-    assert_eq!(map.insert(F(60)), None);
-
-    // Existing key (insert)
-    match map.entry::<A>() {
-        Entry::Vacant(_) => unreachable!(),
-        Entry::Occupied(mut view) => {
-            assert_eq!(view.get(), &A(10));
-            assert_eq!(view.insert(A(100)), A(10));
+#[cfg(test)]
+mod tests {
+    use {AnyMap, Entry};
+
+    #[derive(Clone, Debug, PartialEq)] struct A(i32);
+    #[derive(Clone, Debug, PartialEq)] struct B(i32);
+    #[derive(Clone, Debug, PartialEq)] struct C(i32);
+    #[derive(Clone, Debug, PartialEq)] struct D(i32);
+    #[derive(Clone, Debug, PartialEq)] struct E(i32);
+    #[derive(Clone, Debug, PartialEq)] struct F(i32);
+    #[derive(Clone, Debug, PartialEq)] struct J(i32);
+
+    #[test]
+    fn test_entry() {
+        let mut map: AnyMap = AnyMap::new();
+        assert_eq!(map.insert(A(10)), None);
+        assert_eq!(map.insert(B(20)), None);
+        assert_eq!(map.insert(C(30)), None);
+        assert_eq!(map.insert(D(40)), None);
+        assert_eq!(map.insert(E(50)), None);
+        assert_eq!(map.insert(F(60)), None);
+
+        // Existing key (insert)
+        match map.entry::<A>() {
+            Entry::Vacant(_) => unreachable!(),
+            Entry::Occupied(mut view) => {
+                assert_eq!(view.get(), &A(10));
+                assert_eq!(view.insert(A(100)), A(10));
+            }
         }
-    }
-    assert_eq!(map.get::<A>().unwrap(), &A(100));
-    assert_eq!(map.len(), 6);
+        assert_eq!(map.get::<A>().unwrap(), &A(100));
+        assert_eq!(map.len(), 6);
 
 
-    // Existing key (update)
-    match map.entry::<B>() {
-        Entry::Vacant(_) => unreachable!(),
-        Entry::Occupied(mut view) => {
-            let v = view.get_mut();
-            let new_v = B(v.0 * 10);
-            *v = new_v;
+        // Existing key (update)
+        match map.entry::<B>() {
+            Entry::Vacant(_) => unreachable!(),
+            Entry::Occupied(mut view) => {
+                let v = view.get_mut();
+                let new_v = B(v.0 * 10);
+                *v = new_v;
+            }
         }
-    }
-    assert_eq!(map.get().unwrap(), &B(200));
-    assert_eq!(map.len(), 6);
+        assert_eq!(map.get().unwrap(), &B(200));
+        assert_eq!(map.len(), 6);
 
 
-    // Existing key (remove)
-    match map.entry::<C>() {
-        Entry::Vacant(_) => unreachable!(),
-        Entry::Occupied(view) => {
-            assert_eq!(view.remove(), C(30));
+        // Existing key (remove)
+        match map.entry::<C>() {
+            Entry::Vacant(_) => unreachable!(),
+            Entry::Occupied(view) => {
+                assert_eq!(view.remove(), C(30));
+            }
         }
-    }
-    assert_eq!(map.get::<C>(), None);
-    assert_eq!(map.len(), 5);
+        assert_eq!(map.get::<C>(), None);
+        assert_eq!(map.len(), 5);
 
 
-    // Inexistent key (insert)
-    match map.entry::<J>() {
-        Entry::Occupied(_) => unreachable!(),
-        Entry::Vacant(view) => {
-            assert_eq!(*view.insert(J(1000)), J(1000));
+        // Inexistent key (insert)
+        match map.entry::<J>() {
+            Entry::Occupied(_) => unreachable!(),
+            Entry::Vacant(view) => {
+                assert_eq!(*view.insert(J(1000)), J(1000));
+            }
         }
+        assert_eq!(map.get::<J>().unwrap(), &J(1000));
+        assert_eq!(map.len(), 6);
+
+        // Entry.or_insert on existing key
+        map.entry::<B>().or_insert(B(71)).0 += 1;
+        assert_eq!(map.get::<B>().unwrap(), &B(201));
+        assert_eq!(map.len(), 6);
+
+        // Entry.or_insert on nonexisting key
+        map.entry::<C>().or_insert(C(300)).0 += 1;
+        assert_eq!(map.get::<C>().unwrap(), &C(301));
+        assert_eq!(map.len(), 7);
+    }
+
+    #[cfg(feature = "clone")]
+    #[test]
+    fn test_clone() {
+        let mut map = AnyMap::new();
+        let _ = map.insert(A(1));
+        let _ = map.insert(B(2));
+        let _ = map.insert(D(3));
+        let _ = map.insert(E(4));
+        let _ = map.insert(F(5));
+        let _ = map.insert(J(6));
+        let map2 = map.clone();
+        assert_eq!(map2.len(), 6);
+        assert_eq!(map2.get::<A>(), Some(&A(1)));
+        assert_eq!(map2.get::<B>(), Some(&B(2)));
+        assert_eq!(map2.get::<C>(), None);
+        assert_eq!(map2.get::<D>(), Some(&D(3)));
+        assert_eq!(map2.get::<E>(), Some(&E(4)));
+        assert_eq!(map2.get::<F>(), Some(&F(5)));
+        assert_eq!(map2.get::<J>(), Some(&J(6)));
     }
-    assert_eq!(map.get::<J>().unwrap(), &J(1000));
-    assert_eq!(map.len(), 6);
 }
index 0ce7266bf4fb6df0a0c96e5c4b4c343f214e20c8..e75de32c5f326bc95f1507617ae2ccf06c0d404f 100644 (file)
@@ -2,7 +2,7 @@
 //!
 //! All relevant details are in the `RawAnyMap` struct.
 
-use std::any::{Any, TypeId};
+use std::any::TypeId;
 use std::borrow::Borrow;
 use std::collections::hash_map::{self, HashMap};
 use std::collections::hash_state::HashState;
@@ -13,10 +13,16 @@ use std::mem;
 use std::ops::{Index, IndexMut};
 use std::ptr;
 
+#[cfg(not(feature = "clone"))]
+pub use std::any::Any;
+#[cfg(feature = "clone")]
+pub use with_clone::Any;
+
 struct TypeIdHasher {
     value: u64,
 }
 
+#[cfg_attr(feature = "clone", derive(Clone))]
 struct TypeIdState;
 
 impl HashState for TypeIdState {
@@ -50,6 +56,7 @@ impl Hasher for TypeIdHasher {
 /// contents of an `AnyMap`. However, because you will then be dealing with `Any` trait objects, it
 /// doesn’t tend to be so very useful. Still, if you need it, it’s here.
 #[derive(Debug)]
+#[cfg_attr(feature = "clone", derive(Clone))]
 pub struct RawAnyMap {
     inner: HashMap<TypeId, Box<Any>, TypeIdState>,
 }
index d34f8fe2bbaec9cb1fe9d3c960344df5cf3270a5..bfd7d95154cae9eb52cae8ca73131d6a3912bd38 100644 (file)
@@ -1,4 +1,4 @@
-use std::any::Any;
+use raw::Any;
 use std::mem;
 use std::raw::TraitObject;
 
diff --git a/src/with_clone.rs b/src/with_clone.rs
new file mode 100644 (file)
index 0000000..71b3aea
--- /dev/null
@@ -0,0 +1,37 @@
+use std::fmt;
+
+#[doc(hidden)]
+pub trait CloneToAny {
+    /// Clone `self` into a new `Box<Any>` object.
+    fn clone_to_any(&self) -> Box<Any>;
+}
+
+impl<T: 'static + Clone> CloneToAny for T {
+    fn clone_to_any(&self) -> Box<Any> {
+        Box::new(self.clone())
+    }
+}
+
+#[doc(hidden)]
+/// Pretty much just `std::any::Any + Clone`.
+pub trait Any: ::std::any::Any + CloneToAny { }
+
+impl<T: 'static + Clone> Any for T { }
+
+impl Clone for Box<Any> {
+    fn clone(&self) -> Box<Any> {
+        (**self).clone_to_any()
+    }
+}
+
+impl<'a> fmt::Debug for &'a Any {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.pad("&Any")
+    }
+}
+
+impl<'a> fmt::Debug for Box<Any> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.pad("Box<Any>")
+    }
+}