0.10.0: move Clone functionality into a feature.

No more separate Git branch for it; Cargo features fit the bill well.
This commit is contained in:
Chris Morgan 2015-03-27 11:05:12 +11:00
parent e84d5846bf
commit c6480a9172
7 changed files with 147 additions and 66 deletions

View 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

View 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 = []

View file

@ -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.
Its 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
------

View 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);
#[cfg(test)]
mod tests {
use {AnyMap, 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);
#[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);
// 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));
#[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);
}

View 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
/// doesnt tend to be so very useful. Still, if you need it, its here.
#[derive(Debug)]
#[cfg_attr(feature = "clone", derive(Clone))]
pub struct RawAnyMap {
inner: HashMap<TypeId, Box<Any>, TypeIdState>,
}

View file

@ -1,4 +1,4 @@
use std::any::Any;
use raw::Any;
use std::mem;
use std::raw::TraitObject;

37
src/with_clone.rs Normal file
View file

@ -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>")
}
}