use std::sync;
use crate::error::Error;
use crate::error::Result;
use crate::model::ObjectView;
use super::PartialStore;
use super::Renderable;
use super::Stack;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Interrupt {
Continue,
Break,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct InterruptState {
interrupt: Option<Interrupt>,
}
impl InterruptState {
pub fn interrupted(&self) -> bool {
self.interrupt.is_some()
}
pub fn set_interrupt(&mut self, interrupt: Interrupt) {
self.interrupt = Some(interrupt);
}
pub fn pop_interrupt(&mut self) -> Option<Interrupt> {
let rval = self.interrupt;
self.interrupt = None;
rval
}
}
#[derive(Copy, Clone, Debug)]
struct NullPartials;
impl PartialStore for NullPartials {
fn contains(&self, _name: &str) -> bool {
false
}
fn names(&self) -> Vec<&str> {
Vec::new()
}
fn try_get(&self, _name: &str) -> Option<sync::Arc<dyn Renderable>> {
None
}
fn get(&self, name: &str) -> Result<sync::Arc<dyn Renderable>> {
Err(Error::with_msg("Partial does not exist").context("name", name.to_owned()))
}
}
pub struct RuntimeBuilder<'g> {
globals: Option<&'g dyn ObjectView>,
partials: Option<&'g dyn PartialStore>,
}
impl<'g> RuntimeBuilder<'g> {
pub fn new() -> Self {
Self {
globals: None,
partials: None,
}
}
pub fn set_globals(mut self, values: &'g dyn ObjectView) -> Self {
self.globals = Some(values);
self
}
pub fn set_partials(mut self, values: &'g dyn PartialStore) -> Self {
self.partials = Some(values);
self
}
pub fn build(self) -> Runtime<'g> {
let stack = match self.globals {
Some(globals) => Stack::with_globals(globals),
None => Stack::empty(),
};
let partials = self.partials.unwrap_or(&NullPartials);
Runtime {
stack,
partials,
registers: anymap::AnyMap::new(),
interrupt: InterruptState::default(),
}
}
}
impl<'g> Default for RuntimeBuilder<'g> {
fn default() -> Self {
Self::new()
}
}
pub struct Runtime<'g> {
stack: Stack<'g>,
partials: &'g dyn PartialStore,
registers: anymap::AnyMap,
interrupt: InterruptState,
}
impl<'g> Runtime<'g> {
pub fn new() -> Self {
Runtime::default()
}
pub fn interrupt(&self) -> &InterruptState {
&self.interrupt
}
pub fn interrupt_mut(&mut self) -> &mut InterruptState {
&mut self.interrupt
}
pub fn partials(&self) -> &dyn PartialStore {
self.partials
}
pub fn get_register_mut<T: anymap::any::IntoBox<dyn anymap::any::Any> + Default>(
&mut self,
) -> &mut T {
self.registers.entry::<T>().or_insert_with(Default::default)
}
pub fn stack(&self) -> &Stack<'_> {
&self.stack
}
pub fn stack_mut<'a>(&'a mut self) -> &'a mut Stack<'g>
where
'g: 'a,
{
&mut self.stack
}
pub fn run_in_scope<RvalT, FnT>(&mut self, f: FnT) -> RvalT
where
FnT: FnOnce(&mut Runtime<'_>) -> RvalT,
{
self.stack.push_frame();
let result = f(self);
self.stack.pop_frame();
result
}
pub fn run_in_named_scope<RvalT, S: Into<kstring::KString>, FnT>(
&mut self,
name: S,
f: FnT,
) -> RvalT
where
FnT: FnOnce(&mut Runtime<'_>) -> RvalT,
{
self.stack.push_named_frame(name);
let result = f(self);
self.stack.pop_frame();
result
}
}
impl<'g> Default for Runtime<'g> {
fn default() -> Self {
Self {
stack: Stack::empty(),
partials: &NullPartials,
registers: anymap::AnyMap::new(),
interrupt: InterruptState::default(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::model::value::ValueViewCmp;
use crate::model::Scalar;
use crate::model::Value;
#[test]
fn scoped_variables() {
let test_path = [Scalar::new("test")];
let global_path = [Scalar::new("global")];
let mut rt = Runtime::new();
rt.stack_mut().set_global("test", Value::scalar(42f64));
assert_eq!(
&rt.stack().get(&test_path).unwrap(),
&ValueViewCmp::new(&42f64)
);
rt.run_in_scope(|new_scope| {
assert_eq!(
&new_scope.stack().get(&test_path).unwrap(),
&ValueViewCmp::new(&42f64)
);
new_scope.stack_mut().set("test", Value::scalar(3.14f64));
assert_eq!(
&new_scope.stack().get(&test_path).unwrap(),
&ValueViewCmp::new(&3.14f64)
);
new_scope
.stack_mut()
.set_global("global", Value::scalar("some value"));
});
assert_eq!(
&rt.stack().get(&test_path).unwrap(),
&ValueViewCmp::new(&42f64)
);
assert_eq!(
&rt.stack().get(&global_path).unwrap(),
&ValueViewCmp::new(&"some value")
);
}
}