diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 448c415..e4159c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,4 +33,4 @@ jobs: - name: Run test suite run: | cd repo - cargo test + cargo test --all-features diff --git a/CHANGELOG.md b/CHANGELOG.md index e01525c..912e104 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html>). +## [0.3.0] + +### Added + +* `hashbrown::hash_map` macro + +* `hashbrown::hash_map_e` macro + +* `hashbrown::hash_set` macro + + ## [0.2.6] ### Added @@ -28,6 +39,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `map` macro +* `map_e` macro + * `set` macro diff --git a/Cargo.toml b/Cargo.toml index f1dc2da..af4e635 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,14 +12,33 @@ repository = "https://github.com/jofas/map_macro" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["std"] +std = [] +hashbrown = [] +# feature that allows the docs to link to the hashbrown documentation +__docs = ["dep:hashbrown"] + [dependencies] +hashbrown = { version = "0.14", optional = true } [dev-dependencies] +hashbrown = "0.14" criterion = { version = "^0.3.6", features = ["html_reports"] } [lib] bench = false +[[test]] +name = "hashbrown" +path = "tests/hashbrown.rs" +test = true +required-features = ["hashbrown"] + [[bench]] name = "benches" harness = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/README.md b/README.md index 0913c8f..6fa3305 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,10 @@ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) This crate offers declarative macros for initializing collections from the -[standard library][std]. +[standard library][std] and [hashbrown][hashbrown]. -This crate has zero dependencies. +This crate has zero dependencies and is `#![no_std]` if you opt-out of +support for the standard library collections. ## Example @@ -77,3 +78,4 @@ The explicitly typed versions of the macros are indicated by an `_e` suffix. [type coercion]: https://doc.rust-lang.org/reference/type-coercions.html [object safe]: https://doc.rust-lang.org/reference/items/traits.html#object-safety [hash]: https://doc.rust-lang.org/std/hash/trait.Hash.html +[hashbrown]: https://docs.rs/hashbrown/latest/hashbrown/ diff --git a/src/_std.rs b/src/_std.rs new file mode 100644 index 0000000..75a6263 --- /dev/null +++ b/src/_std.rs @@ -0,0 +1,487 @@ +/// Macro for creating a [`HashMap`](::std::collections::HashMap). +/// +/// Syntactic sugar for [`HashMap::from`](::std::collections::HashMap::from). +/// +/// # Examples +/// +/// ```rust +/// use map_macro::hash_map; +/// +/// let goodbye = hash_map! { +/// "en" => "Goodbye", +/// "de" => "Auf Wiedersehen", +/// "fr" => "Au revoir", +/// "es" => "Adios", +/// }; +/// ``` +/// +#[macro_export] +macro_rules! hash_map { + {$($k: expr => $v: expr),* $(,)?} => { + ::std::collections::HashMap::from([$(($k, $v),)*]) + }; +} + +/// Deprecated. Use [`hash_map!`] instead. +/// +#[deprecated = "deprecated in favour of `hash_map!`. Will be removed in `map-macro v0.3.0`"] +#[macro_export] +macro_rules! map { + {$($k: expr => $v: expr),* $(,)?} => { + ::std::collections::HashMap::from([$(($k, $v),)*]) + }; +} + +/// Explicitly typed equivalent of [`hash_map!`], suitable for +/// [trait object values](crate#explicitly-typed-values-for-trait-objects). +/// +/// # Examples +/// +/// ```rust +/// use std::collections::HashMap; +/// use std::fmt::Debug; +/// +/// use map_macro::hash_map_e; +/// +/// let goodbye: HashMap<&str, &dyn Debug> = hash_map_e! { +/// "en" => &"Goodbye", +/// "de" => &"Auf Wiedersehen", +/// "fr" => &"Au revoir", +/// "es" => &"Adios", +/// }; +/// +/// println!("{:?}", goodbye); +/// ``` +/// +#[macro_export] +macro_rules! hash_map_e { + {$($k: expr => $v: expr),* $(,)?} => { + ::std::collections::HashMap::from([$(($k, $v as _),)*]) + }; +} + +/// Deprecated. Use [`hash_map_e!`] instead. +/// +#[deprecated = "deprecated in favour of `hash_map_e!`. Will be removed in `map-macro v0.3.0`"] +#[macro_export] +macro_rules! map_e { + {$($k: expr => $v: expr),* $(,)?} => { + ::std::collections::HashMap::from([$(($k, $v as _),)*]) + }; +} + +/// Macro for creating a [`BTreeMap`](::std::collections::BTreeMap). +/// +/// Syntactic sugar for [`BTreeMap::from`](::std::collections::BTreeMap::from). +/// +/// # Examples +/// +/// ```rust +/// use map_macro::btree_map; +/// +/// let goodbye = btree_map! { +/// "en" => "Goodbye", +/// "de" => "Auf Wiedersehen", +/// "fr" => "Au revoir", +/// "es" => "Adios", +/// }; +/// ``` +/// +#[macro_export] +macro_rules! btree_map { + {$($k: expr => $v: expr),* $(,)?} => { + ::std::collections::BTreeMap::from([$(($k, $v),)*]) + }; +} + +/// Explicitly typed equivalent of [`btree_map!`], suitable for +/// [trait object values](crate#explicitly-typed-values-for-trait-objects). +/// +/// # Examples +/// +/// ```rust +/// use std::collections::BTreeMap; +/// use std::fmt::Debug; +/// +/// use map_macro::btree_map_e; +/// +/// let goodbye: BTreeMap<&str, &dyn Debug> = btree_map_e! { +/// "en" => &"Goodbye", +/// "de" => &"Auf Wiedersehen", +/// "fr" => &"Au revoir", +/// "es" => &"Adios", +/// }; +/// ``` +/// +#[macro_export] +macro_rules! btree_map_e { + {$($k: expr => $v: expr),* $(,)?} => { + ::std::collections::BTreeMap::from([$(($k, $v as _),)*]) + }; +} + +/// Macro for creating a [`HashSet`](::std::collections::HashSet). +/// +/// Syntactic sugar for [`HashSet::from`](::std::collections::HashSet::from). +/// +/// # Examples +/// +/// ```rust +/// use map_macro::hash_set; +/// +/// let x = hash_set! { 1, 2, 3, 3, 4 }; +/// +/// assert_eq!(x.len(), 4); +/// ``` +/// +#[macro_export] +macro_rules! hash_set { + {$($v: expr),* $(,)?} => { + ::std::collections::HashSet::from([$($v,)*]) + }; +} + +/// Deprecated. Use [`hash_set!`] instead. +/// +#[deprecated = "deprecated in favour of `hash_set!`. Will be removed in `map-macro v0.3.0`"] +#[macro_export] +macro_rules! set { + {$($v: expr),* $(,)?} => { + ::std::collections::HashSet::from([$($v,)*]) + }; +} + +/// Macro for creating a [`BTreeSet`](::std::collections::BTreeSet). +/// +/// Syntactic sugar for [`BTreeSet::from`](::std::collections::BTreeSet::from). +/// +/// # Examples +/// +/// ```rust +/// use map_macro::btree_set; +/// +/// let x = btree_set! { 1, 2, 3, 3, 4 }; +/// +/// assert_eq!(x.len(), 4); +/// ``` +/// +#[macro_export] +macro_rules! btree_set { + {$($v: expr),* $(,)?} => { + ::std::collections::BTreeSet::from([$($v,)*]) + }; +} + +/// Macro for creating a [`VecDeque`](::std::collections::VecDeque). +/// +/// Follows the same syntax as the [`vec!`](::std::vec!) macro. +/// +/// # Examples +/// +/// ``` +/// use map_macro::vec_deque; +/// +/// let v = vec_deque![0, 1, 2, 3]; +/// let v = vec_deque![0; 4]; +/// ``` +/// +#[macro_export] +macro_rules! vec_deque { + {$v: expr; $c: expr} => { + { + let mut vec = ::std::collections::VecDeque::with_capacity($c); + + for _ in 0..$c { + vec.push_back($v); + } + + vec + } + }; + {$($v: expr),* $(,)?} => { + ::std::collections::VecDeque::from([$($v,)*]) + }; +} + +/// Explicitly typed equivalent of [`vec_deque!`], suitable for +/// [trait object values](crate#explicitly-typed-values-for-trait-objects). +/// +/// # Examples +/// +/// ``` +/// use std::collections::VecDeque; +/// use std::fmt::Debug; +/// +/// use map_macro::vec_deque_e; +/// +/// let v: VecDeque<&dyn Debug> = vec_deque_e![&0, &1, &2, &3]; +/// let v: VecDeque<&dyn Debug> = vec_deque_e![&0; 4]; +/// ``` +/// +#[macro_export] +macro_rules! vec_deque_e { + {$v: expr; $c: expr} => { + { + let mut vec = ::std::collections::VecDeque::with_capacity($c); + + for _ in 0..$c { + vec.push_back($v as _); + } + + vec + } + }; + {$($v: expr),* $(,)?} => { + ::std::collections::VecDeque::from([$($v as _,)*]) + }; +} + +/// Macro for creating a [`LinkedList`](::std::collections::LinkedList). +/// +/// Follows the same syntax as the [`vec!`](::std::vec!) macro. +/// +/// # Examples +/// +/// ``` +/// use map_macro::linked_list; +/// +/// let v = linked_list![0, 1, 2, 3]; +/// let v = linked_list![0; 4]; +/// ``` +/// +#[macro_export] +macro_rules! linked_list { + {$v: expr; $c: expr} => { + { + let mut ll = ::std::collections::LinkedList::new(); + + for _ in 0..$c { + ll.push_back($v); + } + + ll + } + }; + {$($v: expr),* $(,)?} => { + ::std::collections::LinkedList::from([$($v,)*]) + }; +} + +/// Explicitly typed equivalent of [`linked_list!`], suitable for +/// [trait object values](crate#explicitly-typed-values-for-trait-objects). +/// +/// # Examples +/// +/// ``` +/// use std::collections::LinkedList; +/// use std::fmt::Debug; +/// +/// use map_macro::linked_list_e; +/// +/// let v: LinkedList<&dyn Debug> = linked_list_e![&0, &1, &2, &3]; +/// let v: LinkedList<&dyn Debug> = linked_list_e![&0; 4]; +/// ``` +/// +#[macro_export] +macro_rules! linked_list_e { + {$v: expr; $c: expr} => { + { + let mut ll = ::std::collections::LinkedList::new(); + + for _ in 0..$c { + ll.push_back($v as _); + } + + ll + } + }; + {$($v: expr),* $(,)?} => { + ::std::collections::LinkedList::from([$($v as _,)*]) + }; +} + +/// Macro for creating a [`BinaryHeap`](::std::collections::BinaryHeap). +/// +/// Follows the same syntax as the [`vec!`](::std::vec!) macro. +/// +/// # Examples +/// +/// ``` +/// use map_macro::binary_heap; +/// +/// let v = binary_heap![0, 1, 2, 3]; +/// let v = binary_heap![0; 4]; +/// ``` +/// +#[macro_export] +macro_rules! binary_heap { + {$v: expr; $c: expr} => { + { + let mut bh = ::std::collections::BinaryHeap::with_capacity($c); + + for _ in 0..$c { + bh.push($v); + } + + bh + } + }; + {$($v: expr),* $(,)?} => { + ::std::collections::BinaryHeap::from([$($v,)*]) + }; +} + +/// Version of the [`vec!`](::std::vec!) macro where the value does not have to implement [`Clone`]. +/// +/// Useful for unclonable types or where `Clone` is exerting undesired behaviour. +/// +/// # Uncloneable Types +/// +/// When using `vec![x; count]`, the type of `x` has to implement `Clone`, because +/// `x` is cloned `count - 1` times into all the vector elements except the first one. +/// For example, calling `vec!` will result in a panic during compile time here, +/// because `UnclonableWrapper` is not cloneable: +/// +/// ```compile_fail +/// struct UnclonableWrapper(u8); +/// +/// let x = vec![UnclonableWrapper(0); 5]; +/// ``` +/// +/// The `vec_no_clone!` macro takes a different approach. +/// Instead of cloning `UnclonableWrapper(0)`, it treats it as an +/// [expression](https://doc.rust-lang.org/reference/expressions.html) which is +/// called 5 times in this case. +/// So 5 independent `UnclonableWrapper` objects, each with its own location in +/// memory, are created: +/// +/// ```rust +/// use map_macro::vec_no_clone; +/// +/// struct UnclonableWrapper(u8); +/// +/// let x = vec_no_clone![UnclonableWrapper(0); 5]; +/// +/// assert_eq!(x.len(), 5); +/// ``` +/// +/// A real-world example where `vec_no_clone!` is a useful drop-in replacement +/// for `vec!` are [atomic types](::std::sync::atomic), which are not clonable: +/// +/// ```rust +/// use std::sync::atomic::AtomicU8; +/// +/// use map_macro::vec_no_clone; +/// +/// let x = vec_no_clone![AtomicU8::new(0); 5]; +/// +/// assert_eq!(x.len(), 5); +/// ``` +/// +/// # Types where `Clone` exerts the wrong Behaviour +/// +/// `vec_no_clone!` is not only useful for unclonable types, but also for types +/// where cloning them is not what you want. +/// The best example would be a reference counted pointer [`Rc`](::std::rc::Rc). +/// When you clone an `Rc`, a new instance referencing the same location in memory +/// is created. +/// If you'd rather have multiple independent reference counted pointers to +/// different memory locations, you can use `vec_no_clone!` as well: +/// +/// ```rust +/// use map_macro::vec_no_clone; +/// +/// use std::cell::RefCell; +/// use std::rc::Rc; +/// +/// // simply clones the reference counted pointer for each element that +/// // is not the first +/// let shared_vec = vec![Rc::new(RefCell::new(0)); 2]; +/// { +/// let mut first = shared_vec[0].borrow_mut(); +/// *first += 1; +/// } +/// +/// assert_eq!(*shared_vec[0].borrow(), 1); +/// +/// // the second element is a clone of the reference counted pointer at +/// // the first element of the vector, referencing the same address in +/// // memory, therefore being mutated as well +/// assert_eq!(*shared_vec[1].borrow(), 1); +/// +/// // the `vec_no_clone!` macro does not clone the object created by the +/// // first expression but instead calls the expression for each element +/// // in the vector, creating two independent objects, each with their +/// // own address in memory +/// let unshared_vec = vec_no_clone![Rc::new(RefCell::new(0)); 2]; +/// +/// { +/// let mut first = unshared_vec[0].borrow_mut(); +/// *first += 1; +/// } +/// +/// assert_eq!(*unshared_vec[0].borrow(), 1); +/// +/// // the second element is not the same cloned reference counted +/// // pointer as it would be if it were constructed with the `vec!` macro +/// // from the standard library like it was above, therefore it is not +/// // mutated +/// assert_eq!(*unshared_vec[1].borrow(), 0); +/// ``` +/// +/// # Drawbacks of using Expressions +/// +/// Since `vec_no_clone!` treats the value as an expression, you must provide the +/// initialization as input directly. +/// This, for example, won't work: +/// +/// ```compile_fail +/// use map_macro::vec_no_clone; +/// +/// struct UnclonableWrapper(u8); +/// +/// let a = UnclonableWrapper(0); +/// +/// // a will have moved into the first element of x, raising a compile +/// // time error for the second element. +/// let x = vec_no_clone![a; 5]; +/// ``` +/// +/// # Processing Lists of Elements +/// +/// You can also use the macro with a list of elements, like `vec!`. +/// In fact, `vec_no_clone!` falls back to `vec!` in this case: +/// +/// ```rust +/// use map_macro::vec_no_clone; +/// +/// let v1 = vec_no_clone![0, 1, 2, 3]; +/// let v2 = vec![0, 1, 2, 3]; +/// +/// assert_eq!(v1, v2); +/// +/// let v1: Vec = vec_no_clone![]; +/// let v2: Vec = vec![]; +/// +/// assert_eq!(v1, v2); +/// ``` +/// +#[macro_export] +macro_rules! vec_no_clone { + {$v: expr; $c: expr} => { + { + let mut vec = Vec::with_capacity($c); + + for _ in 0..$c { + vec.push($v); + } + + vec + } + }; + {$($v: expr),* $(,)?} => { + { + vec![$($v),*] + } + }; +} diff --git a/src/hashbrown.rs b/src/hashbrown.rs new file mode 100644 index 0000000..f1807ba --- /dev/null +++ b/src/hashbrown.rs @@ -0,0 +1,106 @@ +//! Macros for initializing [`hashbrown`] maps and sets. +//! +//! # Supported versions of `hashbrown` +//! +//! As of writing this, up to the current `hashbrown` version `0.14` **all** +//! versions of `hashbrown` are supported. +//! So you can use the macros from this module with any version of `hashbrown` +//! to date. +//! Though highly likely, compatibility can't be guaranteed with future versions +//! of `hashbrown` that break SemVer compatibility. +//! If `hashbrown` were to remove the [`FromIterator`](::core::iter::FromIterator) +//! implementations of `HashMap` and `HashSet` in a release that is SemVer +//! incompatible with `0.14` (i.e. `0.15` or `1.0`) compatibility with the +//! macros from this module would break for that new version. +//! +//! **Note:** to be compatible with all versions of `hashbrown` at once, this +//! crate doesn't re-export `hashbrown`, which means that if you rename +//! `hashbrown` in your dependencies, the macros from this module won't be able +//! to import the types resulting in a compile-time error. +//! + +/// Macro for creating a [`HashMap`](::hashbrown::HashMap). +/// +/// Syntactic sugar for [`HashMap::from_iter`](::hashbrown::HashMap#method.from_iter). +/// +/// # Examples +/// +/// ```rust +/// use map_macro::hashbrown::hash_map; +/// +/// let goodbye = hash_map! { +/// "en" => "Goodbye", +/// "de" => "Auf Wiedersehen", +/// "fr" => "Au revoir", +/// "es" => "Adios", +/// }; +/// ``` +/// +#[doc(hidden)] +#[macro_export] +macro_rules! __hb_hash_map { + {$($k: expr => $v: expr),* $(,)?} => { + <::hashbrown::HashMap::<_, _> as ::core::iter::FromIterator<_>>::from_iter([$(($k, $v),)*]) + }; +} + +/// Explicitly typed equivalent of [`hash_map!`](self::hash_map), suitable for +/// [trait object values](crate#explicitly-typed-values-for-trait-objects). +/// +/// # Examples +/// +/// ```rust +/// use std::fmt::Debug; +/// +/// use hashbrown::HashMap; +/// +/// use map_macro::hashbrown::hash_map_e; +/// +/// let goodbye: HashMap<&str, &dyn Debug> = hash_map_e! { +/// "en" => &"Goodbye", +/// "de" => &"Auf Wiedersehen", +/// "fr" => &"Au revoir", +/// "es" => &"Adios", +/// }; +/// +/// println!("{:?}", goodbye); +/// ``` +/// +#[doc(hidden)] +#[macro_export] +macro_rules! __hb_hash_map_e { + {$($k: expr => $v: expr),* $(,)?} => { + <::hashbrown::HashMap::<_, _> as ::core::iter::FromIterator<_>>::from_iter([$(($k, $v as _),)*]) + }; +} + +/// Macro for creating a [`HashSet`](::hashbrown::HashSet). +/// +/// Syntactic sugar for [`HashSet::from_iter`](::hashbrown::HashSet#method.from_iter). +/// +/// # Examples +/// +/// ```rust +/// use map_macro::hashbrown::hash_set; +/// +/// let x = hash_set! { 1, 2, 3, 3, 4 }; +/// +/// assert_eq!(x.len(), 4); +/// ``` +/// +#[doc(hidden)] +#[macro_export] +macro_rules! __hb_hash_set { + {$($v: expr),* $(,)?} => { + <::hashbrown::HashSet::<_> as ::core::iter::FromIterator<_>>::from_iter([$($v,)*]) + }; +} + +#[doc(inline)] +pub use __hb_hash_map as hash_map; + +#[doc(inline)] +pub use __hb_hash_map_e as hash_map_e; + +#[doc(inline)] +pub use __hb_hash_set as hash_set; diff --git a/src/lib.rs b/src/lib.rs index ac9daf6..c07940f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,489 +1,12 @@ -#![ doc = include_str!("../README.md")] +#![doc = include_str!("../README.md")] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![no_std] -/// Macro for creating a [`HashMap`](std::collections::HashMap). -/// -/// Syntactic sugar for [`HashMap::from`](std::collections::HashMap::from). -/// -/// # Examples -/// -/// ```rust -/// use map_macro::hash_map; -/// -/// let goodbye = hash_map! { -/// "en" => "Goodbye", -/// "de" => "Auf Wiedersehen", -/// "fr" => "Au revoir", -/// "es" => "Adios", -/// }; -/// ``` -/// -#[macro_export] -macro_rules! hash_map { - {$($k: expr => $v: expr),* $(,)?} => { - std::collections::HashMap::from([$(($k, $v),)*]) - }; -} +#[cfg(feature = "std")] +extern crate std; -/// Deprecated. Use [`hash_map!`] instead. -/// -#[deprecated = "deprecated in favour of `hash_map!`. Will be removed in `map-macro v0.3.0`"] -#[macro_export] -macro_rules! map { - {$($k: expr => $v: expr),* $(,)?} => { - std::collections::HashMap::from([$(($k, $v),)*]) - }; -} +#[cfg(feature = "hashbrown")] +pub mod hashbrown; -/// Explicitly typed equivalent of [`hash_map!`], suitable for -/// [trait object values](crate#explicitly-typed-values-for-trait-objects). -/// -/// # Examples -/// -/// ```rust -/// use std::collections::HashMap; -/// use std::fmt::Debug; -/// -/// use map_macro::hash_map_e; -/// -/// let goodbye: HashMap<&str, &dyn Debug> = hash_map_e! { -/// "en" => &"Goodbye", -/// "de" => &"Auf Wiedersehen", -/// "fr" => &"Au revoir", -/// "es" => &"Adios", -/// }; -/// -/// println!("{:?}", goodbye); -/// ``` -/// -#[macro_export] -macro_rules! hash_map_e { - {$($k: expr => $v: expr),* $(,)?} => { - std::collections::HashMap::from([$(($k, $v as _),)*]) - }; -} - -/// Deprecated. Use [`hash_map_e!`] instead. -/// -#[deprecated = "deprecated in favour of `hash_map_e!`. Will be removed in `map-macro v0.3.0`"] -#[macro_export] -macro_rules! map_e { - {$($k: expr => $v: expr),* $(,)?} => { - std::collections::HashMap::from([$(($k, $v as _),)*]) - }; -} - -/// Macro for creating a [`BTreeMap`](std::collections::BTreeMap). -/// -/// Syntactic sugar for [`BTreeMap::from`](std::collections::BTreeMap::from). -/// -/// # Examples -/// -/// ```rust -/// use map_macro::btree_map; -/// -/// let goodbye = btree_map! { -/// "en" => "Goodbye", -/// "de" => "Auf Wiedersehen", -/// "fr" => "Au revoir", -/// "es" => "Adios", -/// }; -/// ``` -/// -#[macro_export] -macro_rules! btree_map { - {$($k: expr => $v: expr),* $(,)?} => { - std::collections::BTreeMap::from([$(($k, $v),)*]) - }; -} - -/// Explicitly typed equivalent of [`btree_map!`], suitable for -/// [trait object values](crate#explicitly-typed-values-for-trait-objects). -/// -/// # Examples -/// -/// ```rust -/// use std::collections::BTreeMap; -/// use std::fmt::Debug; -/// -/// use map_macro::btree_map_e; -/// -/// let goodbye: BTreeMap<&str, &dyn Debug> = btree_map_e! { -/// "en" => &"Goodbye", -/// "de" => &"Auf Wiedersehen", -/// "fr" => &"Au revoir", -/// "es" => &"Adios", -/// }; -/// ``` -/// -#[macro_export] -macro_rules! btree_map_e { - {$($k: expr => $v: expr),* $(,)?} => { - std::collections::BTreeMap::from([$(($k, $v as _),)*]) - }; -} - -/// Macro for creating a [`HashSet`](std::collections::HashSet). -/// -/// Syntactic sugar for [`HashSet::from`](std::collections::HashSet::from). -/// -/// # Examples -/// -/// ```rust -/// use map_macro::hash_set; -/// -/// let x = hash_set! { 1, 2, 3, 3, 4 }; -/// -/// assert_eq!(x.len(), 4); -/// ``` -/// -#[macro_export] -macro_rules! hash_set { - {$($v: expr),* $(,)?} => { - std::collections::HashSet::from([$($v,)*]) - }; -} - -/// Deprecated. Use [`hash_set!`] instead. -/// -#[deprecated = "deprecated in favour of `hash_set!`. Will be removed in `map-macro v0.3.0`"] -#[macro_export] -macro_rules! set { - {$($v: expr),* $(,)?} => { - std::collections::HashSet::from([$($v,)*]) - }; -} - -/// Macro for creating a [`BTreeSet`](std::collections::BTreeSet). -/// -/// Syntactic sugar for [`BTreeSet::from`](std::collections::BTreeSet::from). -/// -/// # Examples -/// -/// ```rust -/// use map_macro::btree_set; -/// -/// let x = btree_set! { 1, 2, 3, 3, 4 }; -/// -/// assert_eq!(x.len(), 4); -/// ``` -/// -#[macro_export] -macro_rules! btree_set { - {$($v: expr),* $(,)?} => { - std::collections::BTreeSet::from([$($v,)*]) - }; -} - -/// Macro for creating a [`VecDeque`](std::collections::VecDeque). -/// -/// Follows the same syntax as the [`vec!`] macro. -/// -/// # Examples -/// -/// ``` -/// use map_macro::vec_deque; -/// -/// let v = vec_deque![0, 1, 2, 3]; -/// let v = vec_deque![0; 4]; -/// ``` -/// -#[macro_export] -macro_rules! vec_deque { - {$v: expr; $c: expr} => { - { - let mut vec = std::collections::VecDeque::with_capacity($c); - - for _ in 0..$c { - vec.push_back($v); - } - - vec - } - }; - {$($v: expr),* $(,)?} => { - std::collections::VecDeque::from([$($v,)*]) - }; -} - -/// Explicitly typed equivalent of [`vec_deque!`], suitable for -/// [trait object values](crate#explicitly-typed-values-for-trait-objects). -/// -/// # Examples -/// -/// ``` -/// use std::collections::VecDeque; -/// use std::fmt::Debug; -/// -/// use map_macro::vec_deque_e; -/// -/// let v: VecDeque<&dyn Debug> = vec_deque_e![&0, &1, &2, &3]; -/// let v: VecDeque<&dyn Debug> = vec_deque_e![&0; 4]; -/// ``` -/// -#[macro_export] -macro_rules! vec_deque_e { - {$v: expr; $c: expr} => { - { - let mut vec = std::collections::VecDeque::with_capacity($c); - - for _ in 0..$c { - vec.push_back($v as _); - } - - vec - } - }; - {$($v: expr),* $(,)?} => { - std::collections::VecDeque::from([$($v as _,)*]) - }; -} - -/// Macro for creating a [`LinkedList`](std::collections::LinkedList). -/// -/// Follows the same syntax as the [`vec!`] macro. -/// -/// # Examples -/// -/// ``` -/// use map_macro::linked_list; -/// -/// let v = linked_list![0, 1, 2, 3]; -/// let v = linked_list![0; 4]; -/// ``` -/// -#[macro_export] -macro_rules! linked_list { - {$v: expr; $c: expr} => { - { - let mut ll = std::collections::LinkedList::new(); - - for _ in 0..$c { - ll.push_back($v); - } - - ll - } - }; - {$($v: expr),* $(,)?} => { - std::collections::LinkedList::from([$($v,)*]) - }; -} - -/// Explicitly typed equivalent of [`linked_list!`], suitable for -/// [trait object values](crate#explicitly-typed-values-for-trait-objects). -/// -/// # Examples -/// -/// ``` -/// use std::collections::LinkedList; -/// use std::fmt::Debug; -/// -/// use map_macro::linked_list_e; -/// -/// let v: LinkedList<&dyn Debug> = linked_list_e![&0, &1, &2, &3]; -/// let v: LinkedList<&dyn Debug> = linked_list_e![&0; 4]; -/// ``` -/// -#[macro_export] -macro_rules! linked_list_e { - {$v: expr; $c: expr} => { - { - let mut ll = std::collections::LinkedList::new(); - - for _ in 0..$c { - ll.push_back($v as _); - } - - ll - } - }; - {$($v: expr),* $(,)?} => { - std::collections::LinkedList::from([$($v as _,)*]) - }; -} - -/// Macro for creating a [`BinaryHeap`](std::collections::BinaryHeap). -/// -/// Follows the same syntax as the [`vec!`] macro. -/// -/// # Examples -/// -/// ``` -/// use map_macro::binary_heap; -/// -/// let v = binary_heap![0, 1, 2, 3]; -/// let v = binary_heap![0; 4]; -/// ``` -/// -#[macro_export] -macro_rules! binary_heap { - {$v: expr; $c: expr} => { - { - let mut bh = std::collections::BinaryHeap::with_capacity($c); - - for _ in 0..$c { - bh.push($v); - } - - bh - } - }; - {$($v: expr),* $(,)?} => { - std::collections::BinaryHeap::from([$($v,)*]) - }; -} - -/// Version of the [`vec!`] macro where the value does not have to implement [`Clone`]. -/// -/// Useful for unclonable types or where `Clone` is exerting undesired behaviour. -/// -/// # Uncloneable Types -/// -/// When using `vec![x; count]`, the type of `x` has to implement `Clone`, because -/// `x` is cloned `count - 1` times into all the vector elements except the first one. -/// For example, calling `vec!` will result in a panic during compile time here, -/// because `UnclonableWrapper` is not cloneable: -/// -/// ```compile_fail -/// struct UnclonableWrapper(u8); -/// -/// let x = vec![UnclonableWrapper(0); 5]; -/// ``` -/// -/// The `vec_no_clone!` macro takes a different approach. -/// Instead of cloning `UnclonableWrapper(0)`, it treats it as an -/// [expression](https://doc.rust-lang.org/reference/expressions.html) which is -/// called 5 times in this case. -/// So 5 independent `UnclonableWrapper` objects, each with its own location in -/// memory, are created: -/// -/// ```rust -/// use map_macro::vec_no_clone; -/// -/// struct UnclonableWrapper(u8); -/// -/// let x = vec_no_clone![UnclonableWrapper(0); 5]; -/// -/// assert_eq!(x.len(), 5); -/// ``` -/// -/// A real-world example where `vec_no_clone!` is a useful drop-in replacement -/// for `vec!` are [atomic types](std::sync::atomic), which are not clonable: -/// -/// ```rust -/// use std::sync::atomic::AtomicU8; -/// -/// use map_macro::vec_no_clone; -/// -/// let x = vec_no_clone![AtomicU8::new(0); 5]; -/// -/// assert_eq!(x.len(), 5); -/// ``` -/// -/// # Types where `Clone` exerts the wrong Behaviour -/// -/// `vec_no_clone!` is not only useful for unclonable types, but also for types -/// where cloning them is not what you want. -/// The best example would be a reference counted pointer [`Rc`](std::rc::Rc). -/// When you clone an `Rc`, a new instance referencing the same location in memory -/// is created. -/// If you'd rather have multiple independent reference counted pointers to -/// different memory locations, you can use `vec_no_clone!` as well: -/// -/// ```rust -/// use map_macro::vec_no_clone; -/// -/// use std::cell::RefCell; -/// use std::rc::Rc; -/// -/// // simply clones the reference counted pointer for each element that -/// // is not the first -/// let shared_vec = vec![Rc::new(RefCell::new(0)); 2]; -/// { -/// let mut first = shared_vec[0].borrow_mut(); -/// *first += 1; -/// } -/// -/// assert_eq!(*shared_vec[0].borrow(), 1); -/// -/// // the second element is a clone of the reference counted pointer at -/// // the first element of the vector, referencing the same address in -/// // memory, therefore being mutated as well -/// assert_eq!(*shared_vec[1].borrow(), 1); -/// -/// // the `vec_no_clone!` macro does not clone the object created by the -/// // first expression but instead calls the expression for each element -/// // in the vector, creating two independent objects, each with their -/// // own address in memory -/// let unshared_vec = vec_no_clone![Rc::new(RefCell::new(0)); 2]; -/// -/// { -/// let mut first = unshared_vec[0].borrow_mut(); -/// *first += 1; -/// } -/// -/// assert_eq!(*unshared_vec[0].borrow(), 1); -/// -/// // the second element is not the same cloned reference counted -/// // pointer as it would be if it were constructed with the `vec!` macro -/// // from the standard library like it was above, therefore it is not -/// // mutated -/// assert_eq!(*unshared_vec[1].borrow(), 0); -/// ``` -/// -/// # Drawbacks of using Expressions -/// -/// Since `vec_no_clone!` treats the value as an expression, you must provide the -/// initialization as input directly. -/// This, for example, won't work: -/// -/// ```compile_fail -/// use map_macro::vec_no_clone; -/// -/// struct UnclonableWrapper(u8); -/// -/// let a = UnclonableWrapper(0); -/// -/// // a will have moved into the first element of x, raising a compile -/// // time error for the second element. -/// let x = vec_no_clone![a; 5]; -/// ``` -/// -/// # Processing Lists of Elements -/// -/// You can also use the macro with a list of elements, like `vec!`. -/// In fact, `vec_no_clone!` falls back to `vec!` in this case: -/// -/// ```rust -/// use map_macro::vec_no_clone; -/// -/// let v1 = vec_no_clone![0, 1, 2, 3]; -/// let v2 = vec![0, 1, 2, 3]; -/// -/// assert_eq!(v1, v2); -/// -/// let v1: Vec = vec_no_clone![]; -/// let v2: Vec = vec![]; -/// -/// assert_eq!(v1, v2); -/// ``` -/// -#[macro_export] -macro_rules! vec_no_clone { - {$v: expr; $c: expr} => { - { - let mut vec = Vec::with_capacity($c); - - for _ in 0..$c { - vec.push($v); - } - - vec - } - }; - {$($v: expr),* $(,)?} => { - { - vec![$($v),*] - } - }; -} +#[cfg(feature = "std")] +mod _std; diff --git a/tests/hashbrown.rs b/tests/hashbrown.rs new file mode 100644 index 0000000..e919ae2 --- /dev/null +++ b/tests/hashbrown.rs @@ -0,0 +1,84 @@ +use hashbrown::HashMap; +use std::fmt::Debug; + +use map_macro::hashbrown::{hash_map, hash_map_e, hash_set}; + +#[derive(Debug)] +struct Dyn1; + +#[derive(Debug)] +struct Dyn2; + +#[test] +fn hash_map1() { + let m = hash_map! { + "en" => "Hello", + "de" => "Hallo", + "fr" => "Bonjour", + "es" => "Hola", + }; + + assert_eq!(m["en"], "Hello"); + assert_eq!(m["de"], "Hallo"); + assert_eq!(m["fr"], "Bonjour"); + assert_eq!(m["es"], "Hola"); +} + +#[test] +fn hash_map2() { + let m = hash_map! { + 0 => "a", + 1 => "b", + 2 => "c", + }; + + assert_eq!(m[&0], "a"); + assert_eq!(m[&1], "b"); + assert_eq!(m[&2], "c"); +} + +#[test] +fn hash_map_e1() { + drop::>(hash_map_e! { + "en" => &"Hello", + "de" => &"Hallo", + "fr" => &"Bonjour", + "es" => &"Hola", + }); +} + +#[test] +fn hash_map_e2() { + drop::>(hash_map_e! { + "1" => &Dyn1, + "2" => &Dyn2, + }); +} + +#[test] +fn hash_set1() { + let s = hash_set! { "a", "b", "c", "d" }; + + assert_eq!(s.len(), 4); + + assert!(s.contains("a")); + assert!(s.contains("b")); + assert!(s.contains("c")); + assert!(s.contains("d")); + + assert!(!s.contains("e")); +} + +#[test] +fn hash_set2() { + let s = hash_set! { 0, 1, 2, 3, 0 }; + + assert_eq!(s.len(), 4); + + assert!(s.contains(&0)); + assert!(s.contains(&1)); + assert!(s.contains(&2)); + assert!(s.contains(&3)); + + assert!(!s.contains(&4)); +}