diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-12-13 23:59:01 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-12-13 23:59:01 +0100 |
| commit | 665b4d2aca81af48b8e0eaca4e709ef2e7825844 (patch) | |
| tree | 4ada33f607455f14b6a170fe4b7fbe173056567b /src/library/maps/mod.rs | |
| parent | 971ff3a2dcff1e68bf7e19017113469aad5a30c2 (diff) | |
More consistent library code and functions 🎄
Diffstat (limited to 'src/library/maps/mod.rs')
| -rw-r--r-- | src/library/maps/mod.rs | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/src/library/maps/mod.rs b/src/library/maps/mod.rs new file mode 100644 index 00000000..284c7181 --- /dev/null +++ b/src/library/maps/mod.rs @@ -0,0 +1,103 @@ +//! Deduplicating maps and keys for argument parsing. + +use std::collections::HashMap; +use std::hash::Hash; + +use crate::func::prelude::*; + +macro_rules! key { + ($type:ty, $name:expr, $($patterns:tt)*) => { + impl $type { + /// Parse this key from an identifier. + pub fn from_ident(ident: &Spanned<Ident>) -> ParseResult<Self> { + Ok(match ident.v.0.as_str() { + $($patterns)* + _ => error!("expected {}", <Self as ExpressionKind>::NAME), + }) + } + } + + impl ExpressionKind for $type { + const NAME: &'static str = $name; + + fn from_expr(expr: Spanned<Expression>) -> ParseResult<Self> { + if let Expression::Ident(ident) = expr.v { + Self::from_ident(&Spanned::new(ident, expr.span)) + } else { + error!("expected {}", Self::NAME); + } + } + } + }; +} + +pub_use_mod!(axis); +pub_use_mod!(alignment); +pub_use_mod!(padding); + +/// A deduplicating map type useful for storing possibly redundant arguments. +#[derive(Debug, Clone, PartialEq)] +pub struct ConsistentMap<K, V> where K: Hash + Eq { + map: HashMap<K, V>, +} + +impl<K, V> ConsistentMap<K, V> where K: Hash + Eq { + pub fn new() -> ConsistentMap<K, V> { + ConsistentMap { map: HashMap::new() } + } + + /// Add a key-value pair. + pub fn add(&mut self, key: K, value: V) -> ParseResult<()> { + match self.map.insert(key, value) { + Some(_) => error!("duplicate argument"), + None => Ok(()) + } + } + + /// Add a key-value pair if the value is not `None`. + pub fn add_opt(&mut self, key: K, value: Option<V>) -> ParseResult<()> { + Ok(if let Some(value) = value { + self.add(key, value)?; + }) + } + + /// Get the value at a key if it is present. + pub fn get(&self, key: K) -> Option<&V> { + self.map.get(&key) + } + + /// Call a function with the value if the key is present. + pub fn with<F>(&self, key: K, callback: F) where F: FnOnce(&V) { + if let Some(value) = self.map.get(&key) { + callback(value); + } + } + + /// Create a new consistent map where keys and values are mapped to new keys + /// and values. + /// + /// Returns an error if a new key is duplicate. + pub fn dedup<F, K2, V2>(&self, f: F) -> LayoutResult<ConsistentMap<K2, V2>> + where F: Fn(&K, &V) -> ParseResult<(K2, V2)>, K2: Hash + Eq { + let mut map = ConsistentMap::new(); + + for (key, value) in self.map.iter() { + let (key, value) = f(key, value)?; + map.add(key, value)?; + } + + Ok(map) + } + + /// Iterate over the (key, value) pairs. + pub fn iter(&self) -> std::collections::hash_map::Iter<'_, K, V> { + self.map.iter() + } +} + +key!(Direction, "direction", + "left-to-right" | "ltr" => LeftToRight, + "right-to-left" | "rtl" => RightToLeft, + "top-to-bottom" | "ttb" => TopToBottom, + "bottom-to-top" | "btt" => BottomToTop, +); |
