use crate::dynamic::{AccessMode, Variant};
use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec::Vec};
use crate::{Dynamic, ImmutableString, StaticVec};
#[derive(Debug, Clone, Hash)]
pub struct Scope<'a> {
values: Vec<Dynamic>,
names: Vec<(Cow<'a, str>, Box<StaticVec<ImmutableString>>)>,
}
impl Default for Scope<'_> {
#[inline(always)]
fn default() -> Self {
Self {
values: Vec::with_capacity(16),
names: Vec::with_capacity(16),
}
}
}
impl<'a> IntoIterator for Scope<'a> {
type Item = (Cow<'a, str>, Dynamic);
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
fn into_iter(self) -> Self::IntoIter {
Box::new(
self.values
.into_iter()
.zip(self.names.into_iter())
.map(|(value, (name, _))| (name, value)),
)
}
}
impl<'a> Scope<'a> {
#[inline(always)]
pub fn new() -> Self {
Default::default()
}
#[inline(always)]
pub fn clear(&mut self) -> &mut Self {
self.names.clear();
self.values.clear();
self
}
#[inline(always)]
pub fn len(&self) -> usize {
self.values.len()
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.values.len() == 0
}
#[inline(always)]
pub fn push(
&mut self,
name: impl Into<Cow<'a, str>>,
value: impl Variant + Clone,
) -> &mut Self {
self.push_dynamic_value(name, AccessMode::ReadWrite, Dynamic::from(value))
}
#[inline(always)]
pub fn push_dynamic(&mut self, name: impl Into<Cow<'a, str>>, value: Dynamic) -> &mut Self {
self.push_dynamic_value(name, value.access_mode(), value)
}
#[inline(always)]
pub fn push_constant(
&mut self,
name: impl Into<Cow<'a, str>>,
value: impl Variant + Clone,
) -> &mut Self {
self.push_dynamic_value(name, AccessMode::ReadOnly, Dynamic::from(value))
}
#[inline(always)]
pub fn push_constant_dynamic(
&mut self,
name: impl Into<Cow<'a, str>>,
value: Dynamic,
) -> &mut Self {
self.push_dynamic_value(name, AccessMode::ReadOnly, value)
}
#[inline]
pub(crate) fn push_dynamic_value(
&mut self,
name: impl Into<Cow<'a, str>>,
access: AccessMode,
mut value: Dynamic,
) -> &mut Self {
self.names.push((name.into(), Box::new(Default::default())));
value.set_access_mode(access);
self.values.push(value.into());
self
}
#[inline(always)]
pub fn rewind(&mut self, size: usize) -> &mut Self {
self.names.truncate(size);
self.values.truncate(size);
self
}
#[inline(always)]
pub fn contains(&self, name: &str) -> bool {
self.names
.iter()
.rev() .any(|(key, _)| name == key.as_ref())
}
#[inline(always)]
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessMode)> {
self.names
.iter()
.enumerate()
.rev() .find_map(|(index, (key, _))| {
if name == key.as_ref() {
Some((index, self.values[index].access_mode()))
} else {
None
}
})
}
#[inline(always)]
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
self.names
.iter()
.enumerate()
.rev()
.find(|(_, (key, _))| name == key.as_ref())
.and_then(|(index, _)| self.values[index].flatten_clone().try_cast())
}
#[inline(always)]
pub fn set_value(&mut self, name: &'a str, value: impl Variant + Clone) -> &mut Self {
match self.get_index(name) {
None => {
self.push(name, value);
}
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name),
Some((index, AccessMode::ReadWrite)) => {
*self.values.get_mut(index).unwrap() = Dynamic::from(value);
}
}
self
}
pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
self.get_index(name)
.and_then(move |(index, access)| match access {
AccessMode::ReadWrite => Some(self.get_mut_by_index(index)),
AccessMode::ReadOnly => None,
})
}
#[inline(always)]
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
self.values.get_mut(index).expect("invalid index in Scope")
}
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub(crate) fn add_entry_alias(
&mut self,
index: usize,
alias: impl Into<ImmutableString> + PartialEq<ImmutableString>,
) -> &mut Self {
let entry = self.names.get_mut(index).expect("invalid index in Scope");
if !entry.1.iter().any(|a| &alias == a) {
entry.1.push(alias.into());
}
self
}
#[inline]
pub(crate) fn clone_visible(&self) -> Self {
let mut entries: Self = Default::default();
self.names
.iter()
.enumerate()
.rev()
.for_each(|(index, (name, alias))| {
if !entries.names.iter().any(|(key, _)| key == name) {
entries.names.push((name.clone(), alias.clone()));
entries.values.push(self.values[index].clone());
}
});
entries
}
#[inline(always)]
#[allow(dead_code)]
pub(crate) fn into_iter(
self,
) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<ImmutableString>)> {
self.names
.into_iter()
.zip(self.values.into_iter())
.map(|((name, alias), value)| (name, value, alias.to_vec()))
}
#[inline(always)]
pub fn iter(&self) -> impl Iterator<Item = (&str, bool, Dynamic)> {
self.iter_raw()
.map(|(name, constant, value)| (name, constant, value.flatten_clone()))
}
#[inline(always)]
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, bool, &Dynamic)> {
self.names
.iter()
.zip(self.values.iter())
.map(|((name, _), value)| (name.as_ref(), value.is_read_only(), value))
}
}
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, Dynamic)> for Scope<'a> {
#[inline(always)]
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
iter.into_iter().for_each(|(name, value)| {
self.names.push((name.into(), Box::new(Default::default())));
self.values.push(value);
});
}
}