use crate::operation::Operation; use std::collections::{hash_map::RandomState, HashMap}; #[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)] pub struct Attributes { #[serde(skip_serializing_if = "HashMap::is_empty")] #[serde(flatten)] inner: HashMap, } impl Attributes { pub fn new() -> Self { Attributes { inner: HashMap::new(), } } pub fn remove_empty_value(&mut self) { self.inner.retain(|_, v| v.is_empty()); } pub fn extend(&mut self, other: Attributes) { self.inner.extend(other.inner); } } impl std::convert::From> for Attributes { fn from(attributes: HashMap) -> Self { Attributes { inner: attributes } } } impl std::ops::Deref for Attributes { type Target = HashMap; fn deref(&self) -> &Self::Target { &self.inner } } impl std::ops::DerefMut for Attributes { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } pub struct AttributesBuilder { inner: Attributes, } impl AttributesBuilder { pub fn new() -> Self { Self { inner: Attributes::default(), } } pub fn bold(mut self) -> Self { self.inner.insert("bold".to_owned(), "true".to_owned()); self } pub fn italic(mut self) -> Self { self.inner.insert("italic".to_owned(), "true".to_owned()); self } pub fn underline(mut self) -> Self { self.inner.insert("underline".to_owned(), "true".to_owned()); self } pub fn build(self) -> Attributes { self.inner } } pub fn attributes_from(operation: &Option) -> Option { match operation { None => None, Some(operation) => operation.attributes(), } } pub fn compose_attributes( op1: &Option, op2: &Option, keep_empty: bool, ) -> Option { let a = attributes_from(op1); let b = attributes_from(op2); if a.is_none() { return b; } if b.is_none() { return None; } let mut attrs_a = a.unwrap_or(Attributes::default()); let attrs_b = b.unwrap_or(Attributes::default()); attrs_a.extend(attrs_b); if !keep_empty { attrs_a.remove_empty_value() } return if attrs_a.is_empty() { None } else { Some(attrs_a) }; } pub fn transform_attributes( op1: &Option, op2: &Option, priority: bool, ) -> Option { let a = attributes_from(op1); let b = attributes_from(op2); if a.is_none() { return b; } if b.is_none() { return None; } if !priority { return b; } let attrs_a = a.unwrap_or(Attributes::default()); let attrs_b = b.unwrap_or(Attributes::default()); let result = attrs_b .iter() .fold(Attributes::new(), |mut attributes, (k, v)| { if attrs_a.contains_key(k) == false { attributes.insert(k.clone(), v.clone()); } attributes }); Some(result) } pub fn invert_attributes(attr: Option, base: Option) -> Attributes { let attr = attr.unwrap_or(Attributes::new()); let base = base.unwrap_or(Attributes::new()); let base_inverted = base .iter() .fold(Attributes::new(), |mut attributes, (k, v)| { if base.get(k) != attr.get(k) && attr.contains_key(k) { attributes.insert(k.clone(), v.clone()); } attributes }); let inverted = attr.iter().fold(base_inverted, |mut attributes, (k, _)| { if base.get(k) != attr.get(k) && !base.contains_key(k) { attributes.insert(k.clone(), "".to_owned()); } attributes }); return inverted; }