From c6480a91723804547a8cc99a2066f64436008abe Mon Sep 17 00:00:00 2001 From: Chris Morgan Date: Fri, 27 Mar 2015 11:05:12 +1100 Subject: [PATCH] 0.10.0: move Clone functionality into a feature. No more separate Git branch for it; Cargo features fit the bill well. --- .travis.yml | 2 + Cargo.toml | 5 +- README.md | 13 +--- src/lib.rs | 149 +++++++++++++++++++++++++++---------------- src/raw.rs | 9 ++- src/unchecked_any.rs | 2 +- src/with_clone.rs | 37 +++++++++++ 7 files changed, 149 insertions(+), 68 deletions(-) create mode 100644 src/with_clone.rs diff --git a/.travis.yml b/.travis.yml index f95fbc0..42f2b63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/Cargo.toml b/Cargo.toml index 7fc8f57..d1e5448 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "anymap" -version = "0.9.13" +version = "0.10.0" authors = ["Chris Morgan "] 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 = [] diff --git a/README.md b/README.md index e76bfab..da5510e 100644 --- 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>``, exposing a nice, easy typed interface, perfectly safe and absolutely robust. +The ``AnyMap`` type is a friendly wrapper around a ``HashMap>``, 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 ------ diff --git a/src/lib.rs b/src/lib.rs index acc0265..e38670b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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::(); /// assert_eq!(data.get::(), 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::() { - 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::() { + 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::().unwrap(), &A(100)); - assert_eq!(map.len(), 6); + assert_eq!(map.get::().unwrap(), &A(100)); + assert_eq!(map.len(), 6); - // Existing key (update) - match map.entry::() { - 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::() { + 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::() { - Entry::Vacant(_) => unreachable!(), - Entry::Occupied(view) => { - assert_eq!(view.remove(), C(30)); + // Existing key (remove) + match map.entry::() { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(view) => { + assert_eq!(view.remove(), C(30)); + } } - } - assert_eq!(map.get::(), None); - assert_eq!(map.len(), 5); + assert_eq!(map.get::(), None); + assert_eq!(map.len(), 5); - // Inexistent key (insert) - match map.entry::() { - Entry::Occupied(_) => unreachable!(), - Entry::Vacant(view) => { - assert_eq!(*view.insert(J(1000)), J(1000)); + // Inexistent key (insert) + match map.entry::() { + Entry::Occupied(_) => unreachable!(), + Entry::Vacant(view) => { + assert_eq!(*view.insert(J(1000)), J(1000)); + } } + assert_eq!(map.get::().unwrap(), &J(1000)); + assert_eq!(map.len(), 6); + + // Entry.or_insert on existing key + map.entry::().or_insert(B(71)).0 += 1; + assert_eq!(map.get::().unwrap(), &B(201)); + assert_eq!(map.len(), 6); + + // Entry.or_insert on nonexisting key + map.entry::().or_insert(C(300)).0 += 1; + assert_eq!(map.get::().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::(), Some(&A(1))); + assert_eq!(map2.get::(), Some(&B(2))); + assert_eq!(map2.get::(), None); + assert_eq!(map2.get::(), Some(&D(3))); + assert_eq!(map2.get::(), Some(&E(4))); + assert_eq!(map2.get::(), Some(&F(5))); + assert_eq!(map2.get::(), Some(&J(6))); } - assert_eq!(map.get::().unwrap(), &J(1000)); - assert_eq!(map.len(), 6); } diff --git a/src/raw.rs b/src/raw.rs index 0ce7266..e75de32 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -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, TypeIdState>, } diff --git a/src/unchecked_any.rs b/src/unchecked_any.rs index d34f8fe..bfd7d95 100644 --- a/src/unchecked_any.rs +++ b/src/unchecked_any.rs @@ -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 index 0000000..71b3aea --- /dev/null +++ b/src/with_clone.rs @@ -0,0 +1,37 @@ +use std::fmt; + +#[doc(hidden)] +pub trait CloneToAny { + /// Clone `self` into a new `Box` object. + fn clone_to_any(&self) -> Box; +} + +impl CloneToAny for T { + fn clone_to_any(&self) -> Box { + Box::new(self.clone()) + } +} + +#[doc(hidden)] +/// Pretty much just `std::any::Any + Clone`. +pub trait Any: ::std::any::Any + CloneToAny { } + +impl Any for T { } + +impl Clone for Box { + fn clone(&self) -> Box { + (**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 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Box") + } +} -- 2.42.0