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: global:
- secure: nR+DJRUQ9v03nNZMpMu1tGKLKBAqdQsTIAr8ffdl+DUEh3b2jvQ+vLLNFLPjsloqhoOXo7cWO7qVpiE4ZOq2lNDURQjdiZGFjh/Y5+xKy2BqFdV7qQ1JoBzsMyx28tQTYz0mtBsACiCYKKb+ddNX5hpwrsjp8cS7htZktA5kbiU= - secure: nR+DJRUQ9v03nNZMpMu1tGKLKBAqdQsTIAr8ffdl+DUEh3b2jvQ+vLLNFLPjsloqhoOXo7cWO7qVpiE4ZOq2lNDURQjdiZGFjh/Y5+xKy2BqFdV7qQ1JoBzsMyx28tQTYz0mtBsACiCYKKb+ddNX5hpwrsjp8cS7htZktA5kbiU=
script: script:
- cargo build --verbose --features clone
- cargo test --verbose --features clone
- cargo build --verbose - cargo build --verbose
- cargo test --verbose - cargo test --verbose
- cargo doc --verbose - cargo doc --verbose

View file

@ -1,6 +1,6 @@
[package] [package]
name = "anymap" name = "anymap"
version = "0.9.13" version = "0.10.0"
authors = ["Chris Morgan <me@chrismorgan.info>"] authors = ["Chris Morgan <me@chrismorgan.info>"]
description = "A safe and convenient store for one value of each type" 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" #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" readme = "README.md"
keywords = ["container", "data-structure", "map"] keywords = ["container", "data-structure", "map"]
license = "MIT/Apache-2.0" 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. 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. What this means is that in an ``AnyMap`` you may store zero or one values for every type.
Instructions 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). 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`.
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.)
Author Author
------ ------

View file

@ -7,10 +7,10 @@
#[cfg(test)] #[cfg(test)]
extern crate test; extern crate test;
use std::any::{Any, TypeId}; use std::any::TypeId;
use std::marker::PhantomData; use std::marker::PhantomData;
use raw::RawAnyMap; use raw::{RawAnyMap, Any};
use unchecked_any::UncheckedAnyExt; use unchecked_any::UncheckedAnyExt;
macro_rules! impl_common_methods { macro_rules! impl_common_methods {
@ -85,6 +85,8 @@ macro_rules! impl_common_methods {
mod unchecked_any; mod unchecked_any;
pub mod raw; pub mod raw;
#[cfg(feature = "clone")]
mod with_clone;
/// A collection containing zero or one values for any given type and allowing convenient, /// A collection containing zero or one values for any given type and allowing convenient,
/// type-safe access to those values. /// type-safe access to those values.
@ -98,7 +100,7 @@ pub mod raw;
/// data.remove::<i32>(); /// data.remove::<i32>();
/// assert_eq!(data.get::<i32>(), None); /// assert_eq!(data.get::<i32>(), None);
/// ///
/// #[derive(PartialEq, Debug)] /// #[derive(Clone, PartialEq, Debug)]
/// struct Foo { /// struct Foo {
/// str: String, /// str: String,
/// } /// }
@ -112,6 +114,7 @@ pub mod raw;
/// ///
/// Values containing non-static references are not permitted. /// Values containing non-static references are not permitted.
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "clone", derive(Clone))]
pub struct AnyMap { pub struct AnyMap {
raw: RawAnyMap, raw: RawAnyMap,
} }
@ -300,16 +303,20 @@ fn bench_get_present(b: &mut ::test::Bencher) {
}) })
} }
#[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] #[test]
fn test_entry() { 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(); let mut map: AnyMap = AnyMap::new();
assert_eq!(map.insert(A(10)), None); assert_eq!(map.insert(A(10)), None);
assert_eq!(map.insert(B(20)), None); assert_eq!(map.insert(B(20)), None);
@ -363,4 +370,36 @@ fn test_entry() {
} }
assert_eq!(map.get::<J>().unwrap(), &J(1000)); assert_eq!(map.get::<J>().unwrap(), &J(1000));
assert_eq!(map.len(), 6); 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)));
}
} }

View file

@ -2,7 +2,7 @@
//! //!
//! All relevant details are in the `RawAnyMap` struct. //! All relevant details are in the `RawAnyMap` struct.
use std::any::{Any, TypeId}; use std::any::TypeId;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::collections::hash_map::{self, HashMap}; use std::collections::hash_map::{self, HashMap};
use std::collections::hash_state::HashState; use std::collections::hash_state::HashState;
@ -13,10 +13,16 @@ use std::mem;
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use std::ptr; use std::ptr;
#[cfg(not(feature = "clone"))]
pub use std::any::Any;
#[cfg(feature = "clone")]
pub use with_clone::Any;
struct TypeIdHasher { struct TypeIdHasher {
value: u64, value: u64,
} }
#[cfg_attr(feature = "clone", derive(Clone))]
struct TypeIdState; struct TypeIdState;
impl HashState for 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 /// 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. /// doesnt tend to be so very useful. Still, if you need it, its here.
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "clone", derive(Clone))]
pub struct RawAnyMap { pub struct RawAnyMap {
inner: HashMap<TypeId, Box<Any>, TypeIdState>, inner: HashMap<TypeId, Box<Any>, TypeIdState>,
} }

View file

@ -1,4 +1,4 @@
use std::any::Any; use raw::Any;
use std::mem; use std::mem;
use std::raw::TraitObject; 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>")
}
}