use core::ops::Deref; use std::rc::Rc; use derive_more::Constructor; use hashbrown::{HashMap, HashSet}; use itertools::Itertools; use crate::engine::Engine; use crate::error::{Error, Result}; use crate::ty::public::Symbol; use super::super::public as p; use super::Value; #[repr(transparent)] #[derive(Constructor, Clone, PartialEq, Debug)] pub struct AttrSet { data: HashMap, } impl From> for AttrSet { fn from(data: HashMap) -> Self { Self { data } } } impl Deref for AttrSet { type Target = HashMap; fn deref(&self) -> &Self::Target { &self.data } } impl AttrSet { pub fn with_capacity(cap: usize) -> Self { AttrSet { data: HashMap::with_capacity(cap), } } pub fn push_attr_force(&mut self, sym: String, val: Value) { self.data.insert(sym, val); } pub fn push_attr(&mut self, sym: String, val: Value) { if self.data.get(&sym).is_some() { todo!() } self.data.insert(sym, val); } pub fn select( &self, mut path: impl DoubleEndedIterator>, ) -> Result { // .ok_or_else(|| Error::EvalError())), let mut data = &self.data; let last = path.nth_back(0).unwrap(); for item in path { let item = item?; let Some(Value::AttrSet(attrs)) = data.get(&item) else { return Err(Error::EvalError(format!( "attribute {} not found", Symbol::from(item) ))); }; data = attrs.as_inner(); } let last = last?; data.get(&last) .cloned() .ok_or_else(|| Error::EvalError(format!("{} not found", Symbol::from(last)))) } pub fn has_attr( &self, mut path: impl DoubleEndedIterator>, ) -> Result { let mut data = &self.data; let last = path.nth_back(0).unwrap(); for item in path { let Some(Value::AttrSet(attrs)) = data.get(&item?) else { return Ok(false); }; data = attrs.as_inner(); } Ok(data.get(&last?).is_some()) } pub fn update(&mut self, other: &AttrSet) { for (k, v) in other.data.iter() { self.push_attr_force(k.clone(), v.clone()) } } pub fn as_inner(&self) -> &HashMap { &self.data } pub fn into_inner(self: Rc) -> Rc> { unsafe { core::mem::transmute(self) } } pub fn from_inner(data: HashMap) -> Self { Self { data } } pub fn eq_impl(&self, other: &AttrSet) -> bool { self.data.iter().len() == other.data.iter().len() && std::iter::zip( self.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)), other.data.iter().sorted_by(|(a, _), (b, _)| a.cmp(b)), ) .all(|((_, v1), (_, v2))| v1.eq_impl(v2)) } pub fn to_public(&self, engine: &Engine, seen: &mut HashSet) -> p::Value { p::Value::AttrSet(p::AttrSet::new( self.data .iter() .map(|(sym, value)| (sym.as_str().into(), value.to_public(engine, seen))) .collect(), )) } }