use std::borrow::Cow;
use std::collections::HashMap;
use serde_json::{to_value, Value};
use crate::context::get_json_pointer;
use crate::errors::{Error, Result};
use crate::renderer::for_loop::{ForLoop, ForLoopState};
use crate::renderer::stack_frame::{FrameContext, FrameType, StackFrame, Val};
use crate::template::Template;
use crate::Context;
#[derive(Debug)]
pub struct UserContext<'a> {
inner: &'a Context,
}
impl<'a> UserContext<'a> {
pub fn new(context: &'a Context) -> Self {
UserContext { inner: context }
}
pub fn find_value(&self, key: &str) -> Option<&'a Value> {
self.inner.get(key)
}
pub fn find_value_by_pointer(&self, pointer: &str) -> Option<&'a Value> {
assert!(pointer.starts_with('/'));
let root = pointer.split('/').nth(1).unwrap().replace("~1", "/").replace("~0", "~");
let rest = &pointer[root.len() + 1..];
self.inner.get(&root).and_then(|val| val.pointer(rest))
}
}
#[derive(Debug)]
pub struct CallStack<'a> {
stack: Vec<StackFrame<'a>>,
context: UserContext<'a>,
}
impl<'a> CallStack<'a> {
pub fn new(context: &'a Context, template: &'a Template) -> CallStack<'a> {
CallStack {
stack: vec![StackFrame::new(FrameType::Origin, "ORIGIN", template)],
context: UserContext::new(context),
}
}
pub fn push_for_loop_frame(&mut self, name: &'a str, for_loop: ForLoop<'a>) {
let tpl = self.stack.last().expect("Stack frame").active_template;
self.stack.push(StackFrame::new_for_loop(name, tpl, for_loop));
}
pub fn push_macro_frame(
&mut self,
namespace: &'a str,
name: &'a str,
context: FrameContext<'a>,
tpl: &'a Template,
) {
self.stack.push(StackFrame::new_macro(name, tpl, namespace, context));
}
pub fn push_include_frame(&mut self, name: &'a str, tpl: &'a Template) {
self.stack.push(StackFrame::new_include(name, tpl));
}
pub fn global_frame_mut(&mut self) -> &mut StackFrame<'a> {
if self.current_frame().kind == FrameType::ForLoop {
for stack_frame in self.stack.iter_mut().rev() {
if stack_frame.kind != FrameType::ForLoop {
return stack_frame;
}
}
unreachable!("Global frame not found when trying to break out of for loop");
} else {
self.current_frame_mut()
}
}
pub fn current_frame_mut(&mut self) -> &mut StackFrame<'a> {
self.stack.last_mut().expect("No current frame exists")
}
pub fn current_frame(&self) -> &StackFrame<'a> {
self.stack.last().expect("No current frame exists")
}
pub fn pop(&mut self) {
self.stack.pop().expect("Mistakenly popped Origin frame");
}
pub fn lookup(&self, key: &str) -> Option<Val<'a>> {
for stack_frame in self.stack.iter().rev() {
let found = stack_frame.find_value(key);
if found.is_some() {
return found;
}
if stack_frame.kind == FrameType::Macro || stack_frame.kind == FrameType::Origin {
break;
}
}
if key.contains('.') {
return self
.context
.find_value_by_pointer(&get_json_pointer(key))
.map(|v| Cow::Borrowed(v));
} else if let Some(value) = self.context.find_value(key) {
return Some(Cow::Borrowed(value));
}
None
}
pub fn add_assignment(&mut self, key: &'a str, global: bool, value: Val<'a>) {
if global {
self.global_frame_mut().insert(key, value);
} else {
self.current_frame_mut().insert(key, value);
}
}
pub fn break_for_loop(&mut self) -> Result<()> {
match self.current_frame_mut().for_loop {
Some(ref mut for_loop) => {
for_loop.break_loop();
Ok(())
}
None => Err(Error::msg("Attempted `break` while not in `for loop`")),
}
}
pub fn increment_for_loop(&mut self) -> Result<()> {
let frame = self.current_frame_mut();
frame.clear_context();
match frame.for_loop {
Some(ref mut for_loop) => {
for_loop.increment();
Ok(())
}
None => Err(Error::msg("Attempted `increment` while not in `for loop`")),
}
}
pub fn continue_for_loop(&mut self) -> Result<()> {
match self.current_frame_mut().for_loop {
Some(ref mut for_loop) => {
for_loop.continue_loop();
Ok(())
}
None => Err(Error::msg("Attempted `continue` while not in `for loop`")),
}
}
pub fn should_break_body(&self) -> bool {
match self.current_frame().for_loop {
Some(ref for_loop) => {
for_loop.state == ForLoopState::Break || for_loop.state == ForLoopState::Continue
}
None => false,
}
}
pub fn should_break_for_loop(&self) -> bool {
match self.current_frame().for_loop {
Some(ref for_loop) => for_loop.state == ForLoopState::Break,
None => false,
}
}
pub fn active_template(&self) -> &'a Template {
self.current_frame().active_template
}
pub fn current_context_cloned(&self) -> Value {
let mut context = HashMap::new();
for frame in self.stack.iter().rev() {
context.extend(frame.context_owned());
if let Some(ref for_loop) = frame.for_loop {
context.insert(
for_loop.value_name.to_string(),
for_loop.get_current_value().into_owned(),
);
if for_loop.is_key_value() {
context.insert(
for_loop.key_name.clone().unwrap(),
Value::String(for_loop.get_current_key()),
);
}
}
if frame.kind == FrameType::Macro {
return to_value(&context).unwrap();
}
}
let mut new_ctx = self.context.inner.clone();
for (key, val) in context {
new_ctx.insert(key, &val)
}
new_ctx.into_json()
}
}