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:
parent
e84d5846bf
commit
c6480a9172
|
@ -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
|
||||||
|
|
|
@ -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 = []
|
||||||
|
|
13
README.md
13
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.
|
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.
|
|
||||||
|
|
||||||
It’s a very simple thing. (The initial implementation time was under ten minutes.)
|
|
||||||
|
|
||||||
Author
|
Author
|
||||||
------
|
------
|
||||||
|
|
61
src/lib.rs
61
src/lib.rs
|
@ -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)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
/// doesn’t tend to be so very useful. Still, if you need it, it’s here.
|
/// doesn’t tend to be so very useful. Still, if you need it, it’s 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>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
37
src/with_clone.rs
Normal 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>")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue