diff --git a/phf/src/lib.rs b/phf/src/lib.rs index 49650749..44f0bfb0 100644 --- a/phf/src/lib.rs +++ b/phf/src/lib.rs @@ -65,6 +65,13 @@ extern crate std as core; #[::proc_macro_hack::proc_macro_hack] pub use phf_macros:: phf_map; +#[cfg(feature = "macros")] +/// Macro to create a `static` (compile-time) [`OrderedMap`]. +/// +/// Requires the `"macros"` feature. Same usage as [`phf_map`]`!`. +#[::proc_macro_hack::proc_macro_hack] +pub use phf_macros::phf_ordered_map; + #[cfg(feature = "macros")] /// Macro to create a `static` (compile-time) [`Set`]. /// @@ -88,6 +95,14 @@ pub use phf_macros:: phf_map; #[::proc_macro_hack::proc_macro_hack] pub use phf_macros::phf_set; +#[cfg(feature = "macros")] +/// Macro to create a `static` (compile-time) [`OrderedSet`]. +/// +/// Requires the `"macros"` feature. Same usage as [`phf_set`]`!`. +#[::proc_macro_hack::proc_macro_hack] +pub use phf_macros::phf_ordered_set; + + use core::ops::Deref; pub use phf_shared::PhfHash; @@ -95,9 +110,15 @@ pub use phf_shared::PhfHash; pub use self::map::Map; #[doc(inline)] pub use self::set::Set; +#[doc(inline)] +pub use self::ordered_map::OrderedMap; +#[doc(inline)] +pub use self::ordered_set::OrderedSet; pub mod map; pub mod set; +pub mod ordered_map; +pub mod ordered_set; // WARNING: this is not considered part of phf's public API and is subject to // change at any time. diff --git a/phf/src/ordered_map.rs b/phf/src/ordered_map.rs new file mode 100644 index 00000000..f91b36cc --- /dev/null +++ b/phf/src/ordered_map.rs @@ -0,0 +1,229 @@ +//! An order-preserving immutable map constructed at compile time. +use core::borrow::Borrow; +use core::iter::IntoIterator; +use core::ops::Index; +use core::fmt; +use core::slice; +use phf_shared::{self, PhfHash, HashKey}; + +use crate::Slice; + +/// An order-preserving immutable map constructed at compile time. +/// +/// Unlike a `Map`, iteration order is guaranteed to match the definition +/// order. +/// +/// ## Note +/// +/// The fields of this struct are public so that they may be initialized by the +/// `phf_ordered_map!` macro and code generation. They are subject to change at +/// any time and should never be accessed directly. +pub struct OrderedMap { + #[doc(hidden)] + pub key: HashKey, + #[doc(hidden)] + pub disps: Slice<(u32, u32)>, + #[doc(hidden)] + pub idxs: Slice, + #[doc(hidden)] + pub entries: Slice<(K, V)>, +} + +impl fmt::Debug for OrderedMap where K: fmt::Debug, V: fmt::Debug { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_map().entries(self.entries()).finish() + } +} + +impl<'a, K, V, T: ?Sized> Index<&'a T> for OrderedMap where T: Eq + PhfHash, K: Borrow { + type Output = V; + + fn index(&self, k: &'a T) -> &V { + self.get(k).expect("invalid key") + } +} + +impl OrderedMap { + /// Returns the number of entries in the `Map`. + pub fn len(&self) -> usize { + self.entries.len() + } + + /// Returns true if the `Map` is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns a reference to the value that `key` maps to. + pub fn get(&self, key: &T) -> Option<&V> + where T: Eq + PhfHash, + K: Borrow + { + self.get_entry(key).map(|e| e.1) + } + + /// Returns a reference to the map's internal static instance of the given + /// key. + /// + /// This can be useful for interning schemes. + pub fn get_key(&self, key: &T) -> Option<&K> + where T: Eq + PhfHash, + K: Borrow + { + self.get_entry(key).map(|e| e.0) + } + + /// Determines if `key` is in the `Map`. + pub fn contains_key(&self, key: &T) -> bool + where T: Eq + PhfHash, + K: Borrow + { + self.get(key).is_some() + } + + /// Returns the index of the key within the list used to initialize + /// the ordered map. + pub fn get_index(&self, key: &T) -> Option + where T: Eq + PhfHash, + K: Borrow + { + self.get_internal(key).map(|(i, _)| i) + } + + /// Returns references to both the key and values at an index + /// within the list used to initialize the ordered map. See `.get_index(key)`. + pub fn index(&self, index: usize) -> Option<(&K, &V)> { + self.entries.get(index).map(|&(ref k, ref v)| (k, v)) + } + + /// Like `get`, but returns both the key and the value. + pub fn get_entry(&self, key: &T) -> Option<(&K, &V)> + where T: Eq + PhfHash, + K: Borrow + { + self.get_internal(key).map(|(_, e)| e) + } + + fn get_internal(&self, key: &T) -> Option<(usize, (&K, &V))> + where T: Eq + PhfHash, + K: Borrow + { + if self.disps.len() == 0 { return None; } //Prevent panic on empty map + let hashes = phf_shared::hash(key, &self.key); + let idx_index = phf_shared::get_index(&hashes, &*self.disps, self.idxs.len()); + let idx = self.idxs[idx_index as usize]; + let entry = &self.entries[idx]; + + let b: &T = entry.0.borrow(); + if b == key { + Some((idx, (&entry.0, &entry.1))) + } else { + None + } + } + + /// Returns an iterator over the key/value pairs in the map. + /// + /// Entries are returned in the same order in which they were defined. + pub fn entries<'a>(&'a self) -> Entries<'a, K, V> { + Entries { iter: self.entries.iter() } + } + + /// Returns an iterator over the keys in the map. + /// + /// Keys are returned in the same order in which they were defined. + pub fn keys<'a>(&'a self) -> Keys<'a, K, V> { + Keys { iter: self.entries() } + } + + /// Returns an iterator over the values in the map. + /// + /// Values are returned in the same order in which they were defined. + pub fn values<'a>(&'a self) -> Values<'a, K, V> { + Values { iter: self.entries() } + } +} + +impl<'a, K, V> IntoIterator for &'a OrderedMap { + type Item = (&'a K, &'a V); + type IntoIter = Entries<'a, K, V>; + + fn into_iter(self) -> Entries<'a, K, V> { + self.entries() + } +} + +/// An iterator over the entries in a `OrderedMap`. +pub struct Entries<'a, K: 'a, V: 'a> { + iter: slice::Iter<'a, (K, V)>, +} + +impl<'a, K, V> Iterator for Entries<'a, K, V> { + type Item = (&'a K, &'a V); + + fn next(&mut self) -> Option<(&'a K, &'a V)> { + self.iter.next().map(|e| (&e.0, &e.1)) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a, K, V> DoubleEndedIterator for Entries<'a, K, V> { + fn next_back(&mut self) -> Option<(&'a K, &'a V)> { + self.iter.next_back().map(|e| (&e.0, &e.1)) + } +} + +impl<'a, K, V> ExactSizeIterator for Entries<'a, K, V> {} + +/// An iterator over the keys in a `OrderedMap`. +pub struct Keys<'a, K: 'a, V: 'a> { + iter: Entries<'a, K, V>, +} + +impl<'a, K, V> Iterator for Keys<'a, K, V> { + type Item = &'a K; + + fn next(&mut self) -> Option<&'a K> { + self.iter.next().map(|e| e.0) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a, K, V> DoubleEndedIterator for Keys<'a, K, V> { + fn next_back(&mut self) -> Option<&'a K> { + self.iter.next_back().map(|e| e.0) + } +} + +impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> {} + +/// An iterator over the values in a `OrderedMap`. +pub struct Values<'a, K: 'a, V: 'a> { + iter: Entries<'a, K, V>, +} + +impl<'a, K, V> Iterator for Values<'a, K, V> { + type Item = &'a V; + + fn next(&mut self) -> Option<&'a V> { + self.iter.next().map(|e| e.1) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a, K, V> DoubleEndedIterator for Values<'a, K, V> { + fn next_back(&mut self) -> Option<&'a V> { + self.iter.next_back().map(|e| e.1) + } +} + +impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> {} diff --git a/phf/src/ordered_set.rs b/phf/src/ordered_set.rs new file mode 100644 index 00000000..dbff7de1 --- /dev/null +++ b/phf/src/ordered_set.rs @@ -0,0 +1,136 @@ +//! An order-preserving immutable set constructed at compile time. +use core::borrow::Borrow; +use core::iter::IntoIterator; +use core::fmt; +use crate::{ordered_map, PhfHash, OrderedMap}; + +/// An order-preserving immutable set constructed at compile time. +/// +/// Unlike a `Set`, iteration order is guaranteed to match the definition +/// order. +/// +/// ## Note +/// +/// The fields of this struct are public so that they may be initialized by the +/// `phf_ordered_set!` macro and code generation. They are subject to change at +/// any time and should never be accessed directly. +pub struct OrderedSet { + #[doc(hidden)] + pub map: OrderedMap, +} + +impl fmt::Debug for OrderedSet where T: fmt::Debug { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_set().entries(self).finish() + } +} + +impl OrderedSet { + /// Returns the number of elements in the `OrderedSet`. + pub fn len(&self) -> usize { + self.map.len() + } + + /// Returns true if the `OrderedSet` contains no elements. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns a reference to the set's internal static instance of the given + /// key. + /// + /// This can be useful for interning schemes. + pub fn get_key(&self, key: &U) -> Option<&T> + where U: Eq + PhfHash, + T: Borrow + { + self.map.get_key(key) + } + + /// Returns the index of the key within the list used to initialize + /// the ordered set. + pub fn get_index(&self, key: &U) -> Option + where U: Eq + PhfHash, + T: Borrow + { + self.map.get_index(key) + } + + /// Returns a reference to the key at an index + /// within the list used to initialize the ordered set. See `.get_index(key)`. + pub fn index(&self, index: usize) -> Option<&T> { + self.map.index(index).map(|(k, &())| k) + } + + /// Returns true if `value` is in the `Set`. + pub fn contains(&self, value: &U) -> bool + where U: Eq + PhfHash, + T: Borrow + { + self.map.contains_key(value) + } + + /// Returns an iterator over the values in the set. + /// + /// Values are returned in the same order in which they were defined. + pub fn iter<'a>(&'a self) -> Iter<'a, T> { + Iter { iter: self.map.keys() } + } +} + +impl OrderedSet where T: Eq + PhfHash { + /// Returns true if `other` shares no elements with `self`. + #[inline] + pub fn is_disjoint(&self, other: &OrderedSet) -> bool { + !self.iter().any(|value| other.contains(value)) + } + + /// Returns true if `other` contains all values in `self`. + #[inline] + pub fn is_subset(&self, other: &OrderedSet) -> bool { + self.iter().all(|value| other.contains(value)) + } + + /// Returns true if `self` contains all values in `other`. + #[inline] + pub fn is_superset(&self, other: &OrderedSet) -> bool { + other.is_subset(self) + } +} + +impl<'a, T> IntoIterator for &'a OrderedSet { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +/// An iterator over the values in a `OrderedSet`. +pub struct Iter<'a, T: 'a> { + iter: ordered_map::Keys<'a, T, ()>, +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + self.iter.next_back() + } +} + +impl<'a, T> ExactSizeIterator for Iter<'a, T> {} diff --git a/phf_codegen/src/lib.rs b/phf_codegen/src/lib.rs index 2f3c428a..9152387d 100644 --- a/phf_codegen/src/lib.rs +++ b/phf_codegen/src/lib.rs @@ -311,3 +311,161 @@ impl<'a, T: FmtConst + 'a> fmt::Display for DisplaySet<'a, T> { write!(f, "{}::Set {{ map: {} }}", self.inner.path, self.inner) } } + +/// A builder for the `phf::OrderedMap` type. +pub struct OrderedMap { + keys: Vec, + values: Vec, + path: String, +} + +impl OrderedMap { + /// Constructs a enw `phf::OrderedMap` builder. + pub fn new() -> OrderedMap { + OrderedMap { + keys: vec![], + values: vec![], + path: String::from("::phf"), + } + } + + /// Set the path to the `phf` crate from the global namespace + pub fn phf_path(&mut self, path: &str) -> &mut OrderedMap { + self.path = path.to_owned(); + self + } + + /// Adds an entry to the builder. + /// + /// `value` will be written exactly as provided in the constructed source. + pub fn entry(&mut self, key: K, value: &str) -> &mut OrderedMap { + self.keys.push(key); + self.values.push(value.to_owned()); + self + } + + /// Calculate the hash parameters and return a struct implementing + /// [`Display`](::std::fmt::Display) which will print the constructed + /// `phf::OrderedMap`. + /// + /// # Panics + /// + /// Panics if there are any duplicate keys. + pub fn build(&self) -> DisplayOrderedMap { + let mut set = HashSet::new(); + for key in &self.keys { + if !set.insert(key) { + panic!("duplicate key `{}`", Delegate(key)); + } + } + + let state = phf_generator::generate_hash(&self.keys); + + DisplayOrderedMap { + path: &self.path, + state, + keys: &self.keys, + values: &self.values, + } + } +} + +/// An adapter for printing a [`OrderedMap`](OrderedMap). +pub struct DisplayOrderedMap<'a, K: 'a> { + path: &'a str, + state: HashState, + keys: &'a [K], + values: &'a [String], +} + +impl<'a, K: FmtConst + 'a> fmt::Display for DisplayOrderedMap<'a, K> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, + "{}::OrderedMap {{ + key: {:?}, + disps: {}::Slice::Static(&[", + self.path, self.state.key, self.path)?; + for &(d1, d2) in &self.state.disps { + write!(f, + " + ({}, {}),", + d1, + d2)?; + } + write!(f, + " + ]), + idxs: {}::Slice::Static(&[", self.path)?; + for &idx in &self.state.map { + write!(f, + " + {},", + idx)?; + } + write!(f, + " + ]), + entries: {}::Slice::Static(&[", self.path)?; + for (key, value) in self.keys.iter().zip(self.values.iter()) { + write!(f, + " + ({}, {}),", + Delegate(key), + value)?; + } + write!(f, + " + ]), +}}") + } +} + +/// A builder for the `phf::OrderedSet` type. +pub struct OrderedSet { + map: OrderedMap, +} + +impl OrderedSet { + /// Constructs a new `phf::OrderedSet` builder. + pub fn new() -> OrderedSet { + OrderedSet { + map: OrderedMap::new(), + } + } + + /// Set the path to the `phf` crate from the global namespace + pub fn phf_path(&mut self, path: &str) -> &mut OrderedSet { + self.map.phf_path(path); + self + } + + /// Adds an entry to the builder. + pub fn entry(&mut self, entry: T) -> &mut OrderedSet { + self.map.entry(entry, "()"); + self + } + + /// Calculate the hash parameters and return a struct implementing + /// [`Display`](::std::fmt::Display) which will print the constructed + /// `phf::OrderedSet`. + /// + /// # Panics + /// + /// Panics if there are any duplicate keys. + pub fn build(&self) -> DisplayOrderedSet { + DisplayOrderedSet { + inner: self.map.build() + } + } +} + +/// An adapter for printing a [`OrderedSet`](OrderedSet). +pub struct DisplayOrderedSet<'a, T: 'a> { + inner: DisplayOrderedMap<'a, T>, +} + +impl<'a, T: FmtConst + 'a> fmt::Display for DisplayOrderedSet<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}::OrderedSet {{ map: {} }}", self.inner.path, self.inner) + } +} diff --git a/phf_codegen/test/build.rs b/phf_codegen/test/build.rs index ebc06be0..14078ab7 100644 --- a/phf_codegen/test/build.rs +++ b/phf_codegen/test/build.rs @@ -32,6 +32,26 @@ fn main() -> io::Result<()> { .build() )?; + writeln!( + &mut file, + "static ORDERED_MAP: ::phf::OrderedMap = \n{};", + phf_codegen::OrderedMap::new() + .entry(1u32, "\"a\"") + .entry(2u32, "\"b\"") + .entry(3u32, "\"c\"") + .build() + )?; + + writeln!( + &mut file, + "static ORDERED_SET: ::phf::OrderedSet = \n{};", + phf_codegen::OrderedSet::new() + .entry(1u32) + .entry(2u32) + .entry(3u32) + .build() + )?; + writeln!( &mut file, "static STR_KEYS: ::phf::Map<&'static str, u32> = \n{};", @@ -56,6 +76,10 @@ fn main() -> io::Result<()> { "static EMPTY: ::phf::Map = \n{};", phf_codegen::Map::::new().build())?; + writeln!(&mut file, + "static EMPTY_ORDERED: ::phf::OrderedMap = \n{};", + phf_codegen::OrderedMap::::new().build())?; + writeln!(&mut file, "static ARRAY_KEYS: ::phf::Map<[u8; 3], u32> = \n{};", phf_codegen::Map::<[u8; 3]>::new() .entry(*b"foo", "0") diff --git a/phf_codegen/test/src/lib.rs b/phf_codegen/test/src/lib.rs index 5e900711..0f5ffcad 100644 --- a/phf_codegen/test/src/lib.rs +++ b/phf_codegen/test/src/lib.rs @@ -20,6 +20,24 @@ mod test { assert!(!SET.contains(&4)); } + #[test] + fn ordered_map() { + assert_eq!("a", ORDERED_MAP[&1]); + assert_eq!("b", ORDERED_MAP[&2]); + assert_eq!("c", ORDERED_MAP[&3]); + assert!(!ORDERED_MAP.contains_key(&100)); + assert_eq!(&["a", "b", "c"][..], &ORDERED_MAP.values().cloned().collect::>()[..]); + } + + #[test] + fn ordered_set() { + assert!(ORDERED_SET.contains(&1)); + assert!(ORDERED_SET.contains(&2)); + assert!(ORDERED_SET.contains(&3)); + assert!(!ORDERED_SET.contains(&4)); + assert_eq!(&[1, 2, 3][..], &ORDERED_SET.iter().cloned().collect::>()[..]); + } + #[test] fn str_keys() { assert_eq!(1, STR_KEYS["a"]); @@ -55,4 +73,11 @@ mod test { fn empty_map() { assert_eq!(None, EMPTY.get(&1)); } + + #[test] + fn empty_ordered_map() { + assert_eq!(None, EMPTY_ORDERED.get(&1)); + } + + } diff --git a/phf_macros/src/lib.rs b/phf_macros/src/lib.rs index 89255aa9..7a804bce 100644 --- a/phf_macros/src/lib.rs +++ b/phf_macros/src/lib.rs @@ -260,6 +260,26 @@ fn build_map(entries: &[Entry], state: HashState) -> proc_macro2::TokenStream { } } +fn build_ordered_map(entries: &[Entry], state: HashState) -> proc_macro2::TokenStream { + let key = state.key; + let disps = state.disps.iter().map(|&(d1, d2)| quote!((#d1, #d2))); + let idxs = state.map.iter().map(|idx| quote!(#idx)); + let entries = entries.iter().map(|entry| { + let key = &entry.key.expr; + let value = &entry.value; + quote!((#key, #value)) + }); + + quote! { + phf::OrderedMap { + key: #key, + disps: phf::Slice::Static(&[#(#disps),*]), + idxs: phf::Slice::Static(&[#(#idxs),*]), + entries: phf::Slice::Static(&[#(#entries),*]), + } + } +} + #[::proc_macro_hack::proc_macro_hack] pub fn phf_map(input: TokenStream) -> TokenStream { let map = parse_macro_input!(input as Map); @@ -276,3 +296,20 @@ pub fn phf_set(input: TokenStream) -> TokenStream { let map = build_map(&set.0, state); quote!(phf::Set { map: #map }).into() } + +#[::proc_macro_hack::proc_macro_hack] +pub fn phf_ordered_map(input: TokenStream) -> TokenStream { + let map = parse_macro_input!(input as Map); + let state = phf_generator::generate_hash(&map.0); + + build_ordered_map(&map.0, state).into() +} + +#[::proc_macro_hack::proc_macro_hack] +pub fn phf_ordered_set(input: TokenStream) -> TokenStream { + let set = parse_macro_input!(input as Set); + let state = phf_generator::generate_hash(&set.0); + + let map = build_ordered_map(&set.0, state); + quote!(phf::OrderedSet { map: #map }).into() +} diff --git a/phf_macros/tests/test.rs b/phf_macros/tests/test.rs index 3b983de8..af8f433e 100644 --- a/phf_macros/tests/test.rs +++ b/phf_macros/tests/test.rs @@ -309,3 +309,212 @@ mod set { } } } + +mod ordered_map { + use phf::phf_ordered_map; + + #[allow(dead_code)] + static TRAILING_COMMA: phf::OrderedMap<&'static str, isize> = phf_ordered_map!( + "foo" => 10, + ); + + #[allow(dead_code)] + static NO_TRAILING_COMMA: phf::OrderedMap<&'static str, isize> = phf_ordered_map!( + "foo" => 10 + ); + + #[test] + fn test_two() { + static MAP: phf::OrderedMap<&'static str, isize> = phf_ordered_map!( + "foo" => 10, + "bar" => 11, + ); + assert!(Some(&10) == MAP.get(&"foo")); + assert!(Some(&11) == MAP.get(&"bar")); + assert_eq!(None, MAP.get(&"asdf")); + assert_eq!(2, MAP.len()); + } + + #[test] + fn test_get_index() { + static MAP: phf::OrderedMap<&'static str, isize> = phf_ordered_map!( + "foo" => 5, + "bar" => 5, + "baz" => 5, + ); + assert_eq!(Some(0), MAP.get_index(&"foo")); + assert_eq!(Some(2), MAP.get_index(&"baz")); + assert_eq!(None, MAP.get_index(&"xyz")); + + assert_eq!(Some(0), MAP.get_index(&*"foo".to_string())); + assert_eq!(Some(2), MAP.get_index(&*"baz".to_string())); + assert_eq!(None, MAP.get_index(&*"xyz".to_string())); + } + + #[test] + fn test_index() { + static MAP: phf::OrderedMap<&'static str, isize> = phf_ordered_map!( + "foo" => 5, + "bar" => 6, + ); + assert_eq!(Some((&"foo", &5)), MAP.index(0)); + assert_eq!(Some((&"bar", &6)), MAP.index(1)); + assert_eq!(None, MAP.index(2)); + } + + #[test] + fn test_entries() { + static MAP: phf::OrderedMap<&'static str, i32> = phf_ordered_map!( + "foo" => 10, + "bar" => 11, + "baz" => 12, + ); + let vec = MAP.entries().map(|(&k, &v)| (k, v)).collect::>(); + assert_eq!(vec, vec!(("foo", 10), ("bar", 11), ("baz", 12))); + } + + #[test] + fn test_keys() { + static MAP: phf::OrderedMap<&'static str, isize> = phf_ordered_map!( + "foo" => 10, + "bar" => 11, + "baz" => 12, + ); + let vec = MAP.keys().map(|&e| e).collect::>(); + assert_eq!(vec, vec!("foo", "bar", "baz")); + } + + #[test] + fn test_values() { + static MAP: phf::OrderedMap<&'static str, i32> = phf_ordered_map!( + "foo" => 10, + "bar" => 11, + "baz" => 12, + ); + let vec = MAP.values().map(|&v| v).collect::>(); + assert_eq!(vec, vec!(10, 11, 12)); + } + + #[test] + fn test_index_ok() { + static MAP: phf::OrderedMap<&'static str, isize> = phf_ordered_map!( + "a" => 0, + ); + assert_eq!(0, MAP["a"]); + } + + #[test] + #[should_panic] + fn test_index_fail() { + static MAP: phf::OrderedMap<&'static str, isize> = phf_ordered_map!( + "a" => 0, + ); + MAP["b"]; + } + + #[test] + fn test_non_static_str_key() { + static MAP: phf::OrderedMap<&'static str, isize> = phf_ordered_map!( + "a" => 0, + ); + assert_eq!(Some(&0), MAP.get(&*"a".to_string())); + } + + #[test] + fn test_into_iterator() { + static MAP: phf::OrderedMap<&'static str, isize> = phf_ordered_map!( + "foo" => 10, + ); + + for (k, v) in &MAP { + assert_eq!(&"foo", k); + assert_eq!(&10, v) + } + } +} + +mod ordered_set { + use phf::phf_ordered_set; + + #[allow(dead_code)] + static TRAILING_COMMA: phf::OrderedSet<&'static str> = phf_ordered_set! { + "foo", + }; + + #[allow(dead_code)] + static NO_TRAILING_COMMA: phf::OrderedSet<&'static str> = phf_ordered_set! { + "foo" + }; + + #[test] + fn test_two() { + static SET: phf::OrderedSet<&'static str> = phf_ordered_set! { + "hello", + "there", + "world", + }; + assert!(SET.contains(&"hello")); + assert!(SET.contains(&"there")); + assert!(SET.contains(&"world")); + assert!(!SET.contains(&"foo")); + assert_eq!(3, SET.len()); + } + + #[test] + fn test_get_index() { + static SET: phf::OrderedSet<&'static str> = phf_ordered_set! { + "foo", + "bar", + "baz", + }; + assert_eq!(Some(0), SET.get_index(&"foo")); + assert_eq!(Some(2), SET.get_index(&"baz")); + assert_eq!(None, SET.get_index(&"xyz")); + + assert_eq!(Some(0), SET.get_index(&*"foo".to_string())); + assert_eq!(Some(2), SET.get_index(&*"baz".to_string())); + assert_eq!(None, SET.get_index(&*"xyz".to_string())); + } + + #[test] + fn test_index() { + static MAP: phf::OrderedSet<&'static str> = phf_ordered_set!( + "foo", + "bar", + ); + assert_eq!(Some(&"foo"), MAP.index(0)); + assert_eq!(Some(&"bar"), MAP.index(1)); + assert_eq!(None, MAP.index(2)); + } + + #[test] + fn test_iter() { + static SET: phf::OrderedSet<&'static str> = phf_ordered_set! { + "hello", + "there", + "world", + }; + let vec = SET.iter().map(|&e| e).collect::>(); + assert_eq!(vec, vec!("hello", "there", "world")); + } + + #[test] + fn test_non_static_str_contains() { + static SET: phf::OrderedSet<&'static str> = phf_ordered_set! { + "hello", + "world", + }; + assert!(SET.contains(&*"hello".to_string())); + } + + #[test] + fn test_into_iterator() { + static SET: phf::OrderedSet<&'static str> = phf_ordered_set!( + "foo", + ); + + for e in &SET { + assert_eq!(&"foo", e); + } + } +}