use super::*;
use crate::Error;
use anyhow::anyhow;
use std::collections::HashMap;
#[derive(Debug)]
pub struct VariableCache {
pub(crate) variable_hash_map: HashMap<i64, Variable>,
}
impl Default for VariableCache {
fn default() -> Self {
Self::new()
}
}
impl VariableCache {
pub fn new() -> Self {
VariableCache {
variable_hash_map: HashMap::new(),
}
}
pub fn cache_variable(
&mut self,
parent_key: Option<i64>,
cache_variable: Variable,
core: &mut Core<'_>,
) -> Result<Variable, Error> {
let mut variable_to_add = cache_variable.clone();
if let Some(new_parent_key) = parent_key {
if self.variable_hash_map.contains_key(&new_parent_key) {
variable_to_add.parent_key = parent_key;
} else {
return Err(anyhow!("VariableCache: Attempted to add a new variable: {} with non existent `parent_key`: {}. Please report this as a bug", variable_to_add.name, new_parent_key).into());
}
}
let stored_key = if variable_to_add.variable_key == 0 {
variable_to_add.variable_key = get_sequential_key();
tracing::trace!(
"VariableCache: Add Variable: key={}, parent={:?}, name={:?}",
variable_to_add.variable_key,
variable_to_add.parent_key,
&variable_to_add.name
);
let new_entry_key = variable_to_add.variable_key;
if let Some(old_variable) = self
.variable_hash_map
.insert(variable_to_add.variable_key, variable_to_add)
{
return Err(anyhow!("Attempt to insert a new `Variable`:{:?} with a duplicate cache key: {}. Please report this as a bug.", cache_variable.name, old_variable.variable_key).into());
}
new_entry_key
} else {
tracing::trace!(
"VariableCache: Update Variable, key={}, name={:?}",
variable_to_add.variable_key,
&variable_to_add.name
);
let updated_entry_key = variable_to_add.variable_key;
if let Some(prev_entry) = self
.variable_hash_map
.get_mut(&variable_to_add.variable_key)
{
if &variable_to_add != prev_entry {
tracing::trace!("Updated: {:?}", variable_to_add);
tracing::trace!("Previous: {:?}", prev_entry);
}
*prev_entry = variable_to_add
} else {
return Err(anyhow!("Attempt to update and existing `Variable`:{:?} with a non-existent cache key: {}. Please report this as a bug.", cache_variable.name, variable_to_add.variable_key).into());
}
updated_entry_key
};
if let Some(mut stored_variable) = self.get_variable_by_key(stored_key) {
if !(stored_variable.variable_node_type == VariableNodeType::SvdPeripheral
|| stored_variable.variable_node_type == VariableNodeType::SvdRegister
|| stored_variable.variable_node_type == VariableNodeType::SvdField)
{
stored_variable.extract_value(core, self);
}
if self
.variable_hash_map
.insert(stored_variable.variable_key, stored_variable.clone())
.is_none()
{
Err(anyhow!("Failed to store variable at variable_cache_key: {}. Please report this as a bug.", stored_key).into())
} else {
Ok(stored_variable)
}
} else {
Err(anyhow!(
"Failed to store variable at variable_cache_key: {}. Please report this as a bug.",
stored_key
)
.into())
}
}
pub fn get_variable_by_key(&self, variable_key: i64) -> Option<Variable> {
self.variable_hash_map.get(&variable_key).cloned()
}
pub fn get_variable_by_name_and_parent(
&self,
variable_name: &VariableName,
parent_key: Option<i64>,
) -> Option<Variable> {
let child_variables = self
.variable_hash_map
.values()
.filter(|child_variable| {
&child_variable.name == variable_name && child_variable.parent_key == parent_key
})
.cloned()
.collect::<Vec<Variable>>();
match child_variables.len() {
0 => None,
1 => child_variables.first().cloned(),
child_count => {
tracing::error!("Found {} variables with parent_key={:?} and name={}. Please report this as a bug.", child_count, parent_key, variable_name);
child_variables.last().cloned()
}
}
}
pub fn get_variable_by_name(&self, variable_name: &VariableName) -> Option<Variable> {
let child_variables = self
.variable_hash_map
.values()
.filter(|child_variable| &child_variable.name == variable_name)
.cloned()
.collect::<Vec<Variable>>();
match child_variables.len() {
0 => None,
1 => child_variables.first().cloned(),
child_count => {
tracing::warn!(
"Found {} variables with name={}. Please report this as a bug.",
child_count,
variable_name
);
child_variables.first().cloned()
}
}
}
pub fn get_children(&self, parent_key: Option<i64>) -> Result<Vec<Variable>, Error> {
let mut children: Vec<Variable> = self
.variable_hash_map
.values()
.filter(|child_variable| child_variable.parent_key == parent_key)
.cloned()
.collect::<Vec<Variable>>();
children.sort_by_key(|var| var.variable_key);
Ok(children)
}
pub fn has_children(&self, parent_variable: &Variable) -> Result<bool, Error> {
self.get_children(Some(parent_variable.variable_key))
.map(|children| !children.is_empty())
}
pub fn adopt_grand_children(
&mut self,
parent_variable: &Variable,
obsolete_child_variable: &Variable,
) -> Result<(), Error> {
if obsolete_child_variable.type_name == VariableType::Unknown
|| obsolete_child_variable.variable_node_type != VariableNodeType::DoNotRecurse
{
self.variable_hash_map
.values_mut()
.filter(|search_variable| {
search_variable.parent_key == Some(obsolete_child_variable.variable_key)
})
.for_each(|grand_child| {
grand_child.parent_key = Some(parent_variable.variable_key)
});
self.remove_cache_entry(obsolete_child_variable.variable_key)?;
}
Ok(())
}
pub fn remove_cache_entry_children(&mut self, parent_variable_key: i64) -> Result<(), Error> {
let children: Vec<Variable> = self
.variable_hash_map
.values()
.filter(|search_variable| search_variable.parent_key == Some(parent_variable_key))
.cloned()
.collect();
for child in children {
if self.variable_hash_map.remove(&child.variable_key).is_none() {
return Err(anyhow!("Failed to remove a `VariableCache` entry with key: {}. Please report this as a bug.", child.variable_key).into());
};
}
Ok(())
}
pub fn remove_cache_entry(&mut self, variable_key: i64) -> Result<(), Error> {
self.remove_cache_entry_children(variable_key)?;
if self.variable_hash_map.remove(&variable_key).is_none() {
return Err(anyhow!("Failed to remove a `VariableCache` entry with key: {}. Please report this as a bug.", variable_key).into());
};
Ok(())
}
}