use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard, Weak};
use indexmap::IndexMap;
use thiserror::Error;
use crate::context::IExpressionContext;
use crate::util::Utf16String;
use super::{
ExpressionObjectNames, IExpressionObjectFactory, IExpressionObjects, StandardExpressionResult,
TemplateValue,
};
pub struct ExpressionObjects {
context: Weak<dyn IExpressionContext>,
expression_object_factory: Arc<dyn IExpressionObjectFactory>,
expression_object_names: ExpressionObjectNames,
objects: RwLock<IndexMap<Option<Utf16String>, Option<Arc<TemplateValue>>>>,
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum ExpressionObjectsError {
#[error("{message}")]
IllegalArgument {
message: &'static str,
},
}
impl ExpressionObjects {
pub fn new(
context: Option<Weak<dyn IExpressionContext>>,
expression_object_factory: Option<Arc<dyn IExpressionObjectFactory>>,
) -> Result<Self, ExpressionObjectsError> {
let context = context.ok_or(ExpressionObjectsError::IllegalArgument {
message: "Context cannot be null",
})?;
let expression_object_factory =
expression_object_factory.ok_or(ExpressionObjectsError::IllegalArgument {
message: "Expression Object Factory cannot be null",
})?;
let expression_object_names: ExpressionObjectNames = expression_object_factory
.get_all_expression_object_names()
.unwrap_or_else(|| Arc::from([]));
Ok(Self {
context,
expression_object_factory,
expression_object_names,
objects: RwLock::new(IndexMap::with_capacity(3)),
})
}
}
impl IExpressionObjects for ExpressionObjects {
fn size(&self) -> i32 {
i32::try_from(self.expression_object_names.len()).unwrap_or(i32::MAX)
}
fn contains_object(&self, name: Option<&Utf16String>) -> bool {
self.expression_object_names
.iter()
.any(|candidate| candidate.as_ref() == name)
}
fn get_object_names(&self) -> ExpressionObjectNames {
Arc::clone(&self.expression_object_names)
}
fn get_object(
&self,
name: Option<&Utf16String>,
) -> StandardExpressionResult<Option<Arc<TemplateValue>>> {
let key = name.cloned();
if let Some(object) = read_recovering_poison(&self.objects).get(&key) {
return Ok(object.clone());
}
if !self
.expression_object_names
.iter()
.any(|candidate| candidate == &key)
{
return Ok(None);
}
let Some(context) = self.context.upgrade() else {
return Ok(None);
};
let object = self.expression_object_factory.build_object(context, name)?;
if !self.expression_object_factory.is_cacheable(name) {
return Ok(object);
}
write_recovering_poison(&self.objects).insert(key, object.clone());
Ok(object)
}
}
fn read_recovering_poison<T>(lock: &RwLock<T>) -> RwLockReadGuard<'_, T> {
lock.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
fn write_recovering_poison<T>(lock: &RwLock<T>) -> RwLockWriteGuard<'_, T> {
lock.write()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}