From 14e369449e6d9b4e75a9093e3f13d766ba6542ec Mon Sep 17 00:00:00 2001 From: trivernis Date: Mon, 14 Mar 2022 20:53:54 +0100 Subject: [PATCH] Add initial map implementations Signed-off-by: trivernis --- .gitignore | 3 ++ Cargo.toml | 12 ++++++ README.md | 33 +++++++++++++++ src/base.rs | 38 +++++++++++++++++ src/lib.rs | 11 +++++ src/macros.rs | 49 ++++++++++++++++++++++ src/tests.rs | 72 +++++++++++++++++++++++++++++++++ src/trait_maps/clone_typemap.rs | 33 +++++++++++++++ src/trait_maps/mod.rs | 5 +++ src/trait_maps/typemap.rs | 22 ++++++++++ src/typemap_trait.rs | 44 ++++++++++++++++++++ 11 files changed, 322 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/base.rs create mode 100644 src/lib.rs create mode 100644 src/macros.rs create mode 100644 src/tests.rs create mode 100644 src/trait_maps/clone_typemap.rs create mode 100644 src/trait_maps/mod.rs create mode 100644 src/trait_maps/typemap.rs create mode 100644 src/typemap_trait.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..408b8a5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +.idea \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..de29c9e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "trait-bound-typemap" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" +readme = "README.md" +description = "A crate to create typemaps with additional trait restrictions and implementations" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +multi-trait-object = "0.2.0" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..92615f6 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Trait bound Typemap + +This crate offers typemaps that restrict a given type in their +trait and therefore offer additional trait implementations such as `Clone`. + +## Usage + +```rust +use trait_bound_typemap::{CloneTypeMap, TypeMapTrait, TypeMapKey}; + +#[derive(Clone)] +pub struct MyStruct { + a: u8, + b: String, +} + +pub struct MyStructKey; + +impl TypeMapKey for MyStructKey { + type Value = MyStruct; +} + +fn main() { + let mut map = CloneTypeMap::new(); + let value = MyStruct {a: 5, b: String::from("Hello World")}; + map.insert::(value); + assert!(map.contains_key::()); +} +``` + +## License + +Apache-2.0 \ No newline at end of file diff --git a/src/base.rs b/src/base.rs new file mode 100644 index 0000000..953580f --- /dev/null +++ b/src/base.rs @@ -0,0 +1,38 @@ +use multi_trait_object::MultitraitObject; +use std::any::TypeId; +use std::collections::HashMap; + +#[derive(Debug, Default)] +pub(crate) struct TypeMapBase(pub(crate) HashMap); + +impl TypeMapBase { + #[inline] + pub fn new() -> Self { + Self::default() + } + + #[inline] + pub fn insert(&mut self, value: MultitraitObject) { + self.0.insert(TypeId::of::(), value); + } + + #[inline] + pub fn get(&self) -> Option<&MultitraitObject> { + self.0.get(&TypeId::of::()) + } + + #[inline] + pub fn get_mut(&mut self) -> Option<&mut MultitraitObject> { + self.0.get_mut(&TypeId::of::()) + } + + #[inline] + pub fn remove(&mut self) -> Option { + self.0.remove(&TypeId::of::()) + } + + #[inline] + pub fn contains_key(&self) -> bool { + self.0.contains_key(&TypeId::of::()) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a92c966 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,11 @@ +#![doc=include_str!("../README.md")] + +mod base; +pub(crate) mod macros; +#[cfg(test)] +mod tests; +mod trait_maps; +mod typemap_trait; + +pub use trait_maps::*; +pub use typemap_trait::*; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..ae1a546 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,49 @@ +#[doc(hidden)] +#[macro_export] +macro_rules! impl_typemap { + ($map:ident, $key:ty) => { + pub struct $map($crate::base::TypeMapBase); + + impl $crate::typemap_trait::TypeMapTrait for $map { + type Key = $key; + + #[inline] + fn new() -> Self { + Self($crate::base::TypeMapBase::new()) + } + + #[inline] + fn insert>( + &mut self, + value: T::Value, + ) { + let mto = value.into_mto(); + self.0.insert::(mto); + } + + #[inline] + fn get>(&self) -> Option<&T::Value> { + self.0.get::().and_then(|v| v.downcast_ref()) + } + + #[inline] + fn get_mut>( + &mut self, + ) -> Option<&mut T::Value> { + self.0.get_mut::().and_then(|v| v.downcast_mut()) + } + + #[inline] + fn remove>( + &mut self, + ) -> Option { + self.0.remove::().and_then(|v| v.downcast()) + } + + #[inline] + fn contains_key>(&self) -> bool { + self.0.contains_key::() + } + } + }; +} diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..7af5cb4 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,72 @@ +use crate::{CloneTypeMap, TypeMap, TypeMapKey, TypeMapTrait}; + +pub struct TestStructKey; +#[derive(Clone, Debug, PartialEq)] +pub struct TestStruct { + a: u64, + b: String, +} + +impl Default for TestStruct { + fn default() -> Self { + Self { + a: 46, + b: String::from("Hello World"), + } + } +} + +impl TypeMapKey for TestStructKey { + type Value = TestStruct; +} + +pub struct TestStruct2Key; +#[derive(Clone, Debug, PartialEq)] +pub struct TestStruct2 { + c: i64, + d: bool, +} + +impl Default for TestStruct2 { + fn default() -> Self { + Self { c: 12, d: true } + } +} + +impl TypeMapKey for TestStruct2Key { + type Value = TestStruct2; +} + +pub struct NoCloneStruct; + +impl TypeMapKey for NoCloneStruct { + type Value = NoCloneStruct; +} + +pub struct NotInsertedKey; + +impl TypeMapKey for NotInsertedKey { + type Value = (); +} + +#[test] +fn it_creates_any_maps() { + let mut map = TypeMap::new(); + map.insert::(NoCloneStruct); + map.insert::(TestStruct::default()); + map.insert::(TestStruct2::default()); + assert!(map.contains_key::()); + assert!(map.contains_key::()); + assert!(map.contains_key::()); + assert_eq!(map.contains_key::(), false); +} + +#[test] +fn it_creates_clonable_maps() { + let mut map = CloneTypeMap::new(); + map.insert::(TestStruct::default()); + map.insert::(TestStruct2::default()); + assert!(map.contains_key::()); + assert!(map.contains_key::()); + assert_eq!(map.contains_key::(), false); +} diff --git a/src/trait_maps/clone_typemap.rs b/src/trait_maps/clone_typemap.rs new file mode 100644 index 0000000..82b22a3 --- /dev/null +++ b/src/trait_maps/clone_typemap.rs @@ -0,0 +1,33 @@ +use crate::base::TypeMapBase; +use crate::{impl_typemap, TypeMapKey, TypedKeyMto, TypedKeyTrait}; +use multi_trait_object::{create_object, MultitraitObject, RawClone, TryClone}; + +pub struct CloneTypeMapKey; + +impl TypedKeyTrait for T +where + T: TypeMapKey, + ::Value: TypedKeyMto + Clone, +{ + type Value = T::Value; +} + +impl TypedKeyMto for T { + fn into_mto(self) -> MultitraitObject { + create_object!(self, dyn RawClone) + } +} + +impl_typemap!(CloneTypeMap, CloneTypeMapKey); + +impl Clone for CloneTypeMap { + fn clone(&self) -> Self { + let map = self + .0 + .0 + .iter() + .map(|(t, o)| (t.clone(), o.try_clone().unwrap())) + .collect(); + CloneTypeMap(TypeMapBase(map)) + } +} diff --git a/src/trait_maps/mod.rs b/src/trait_maps/mod.rs new file mode 100644 index 0000000..4299cce --- /dev/null +++ b/src/trait_maps/mod.rs @@ -0,0 +1,5 @@ +mod clone_typemap; +mod typemap; + +pub use clone_typemap::*; +pub use typemap::*; diff --git a/src/trait_maps/typemap.rs b/src/trait_maps/typemap.rs new file mode 100644 index 0000000..4ea5649 --- /dev/null +++ b/src/trait_maps/typemap.rs @@ -0,0 +1,22 @@ +use crate::{impl_typemap, TypeMapKey, TypedKeyMto, TypedKeyTrait}; +use multi_trait_object::{create_object, MultitraitObject}; +use std::any::Any; + +#[derive(Eq, PartialEq, Hash)] +pub struct AnyTypeMapKey; + +impl TypedKeyTrait for T +where + T: TypeMapKey, + ::Value: Any, +{ + type Value = T::Value; +} + +impl TypedKeyMto for T { + fn into_mto(self) -> MultitraitObject { + create_object!(self, dyn Any) + } +} + +impl_typemap!(TypeMap, AnyTypeMapKey); diff --git a/src/typemap_trait.rs b/src/typemap_trait.rs new file mode 100644 index 0000000..51236e8 --- /dev/null +++ b/src/typemap_trait.rs @@ -0,0 +1,44 @@ +use multi_trait_object::MultitraitObject; +use std::any::Any; + +/// A trait that allows using the object implementing it +/// to be used as a type key. +pub trait TypeMapKey: 'static { + type Value: Any; +} + +/// A trait used for restricting values inserted in a type map +/// using type checking +pub trait TypedKeyTrait: 'static { + type Value: TypedKeyMto; +} + +/// A trait used to create a multitrait-object from a given +/// value with the given guaranteed trait implementations +pub trait TypedKeyMto { + fn into_mto(self) -> MultitraitObject; +} + +/// A trait implemented by all typemaps that provides +/// all basic typemap functions +pub trait TypeMapTrait { + type Key; + + /// Creates a new typemap + fn new() -> Self; + + /// Inserts a value into the typemap with the given key + fn insert>(&mut self, value: T::Value); + + /// Returns a reference to a value from the type map with the given provided key + fn get>(&self) -> Option<&T::Value>; + + /// Returns a mutable reference to a value from the type map with the given provided key + fn get_mut>(&mut self) -> Option<&mut T::Value>; + + /// Removes a value from the map for the given key + fn remove>(&mut self) -> Option; + + /// Returns if the map contains a given key + fn contains_key>(&self) -> bool; +}