Skip to content

Commit

Permalink
0.10.0: move Clone functionality into a feature.
Browse files Browse the repository at this point in the history
No more separate Git branch for it; Cargo features fit the bill well.
  • Loading branch information
chris-morgan committed Mar 27, 2015
1 parent e84d584 commit c6480a9
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 68 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "anymap"
version = "0.9.13"
version = "0.10.0"
authors = ["Chris Morgan <[email protected]>"]
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"
Expand All @@ -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 = []
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
------
Expand Down
149 changes: 94 additions & 55 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand All @@ -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,
/// }
Expand All @@ -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,
}
Expand Down Expand Up @@ -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);
}
9 changes: 8 additions & 1 deletion src/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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>,
}
Expand Down
2 changes: 1 addition & 1 deletion src/unchecked_any.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::any::Any;
use raw::Any;
use std::mem;
use std::raw::TraitObject;

Expand Down
37 changes: 37 additions & 0 deletions src/with_clone.rs
Original file line number Diff line number Diff line change
@@ -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>")
}
}

0 comments on commit c6480a9

Please sign in to comment.