1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::*;
use std::fmt::Debug;

mod dynamic;
pub use dynamic::*;

mod scoped;
mod standard;

pub(crate) use scoped::ScopedContext;
pub use standard::StandardContext;

#[cfg(feature = "shared-context")]
mod shared;
#[cfg(feature = "shared-context")]
pub use shared::SharedContext;

/// The primary context trait
pub trait Context: Sized {
    #[doc(hidden)]
    fn set_path_inner(&self, path: &[&Document], doc: ContextMapValue) -> Result<()>;
    #[doc(hidden)]
    fn get_path_inner(&self, path: &[&Document], ctx: &impl Context) -> Data;
    #[doc(hidden)]
    fn wrap(&self) -> ContextWrapper;

    /// Merge the data into the root context
    #[inline]
    fn merge<T: Into<Document>>(&self, doc: T) -> Result<()> {
        match doc.into() {
            Document::Map(m) => {
                for (k, v) in m.into_iter() {
                    let orig = self.get_path(&[&k]).into_result().unwrap_or_default();
                    self.set_path(&[&k], orig + v)?;
                }
                Ok(())
            }
            _ => Err(TemplarError::ParseFailure(
                "Cannot merge a non-mapping into the root context".into(),
            )),
        }
    }

    /// Merge the data into the context at the specified path
    #[inline]
    fn merge_path<T>(&self, path: &[&Document], doc: T) -> Result<()>
    where
        Document: From<T>,
    {
        let orig = self.get_path(path).into_result()?;
        self.set_path::<Document>(path, orig + Document::from(doc))?;
        Ok(())
    }

    /// Set the root context value
    #[inline]
    fn set<T: Into<ContextMapValue>>(&self, doc: T) -> Result<()> {
        self.set_path_inner(&[], doc.into())
    }

    /// Enter a new scope
    #[inline]
    fn create_scope(&self) -> ScopedContext {
        ScopedContext::new(self.wrap())
    }

    /// Set the value at the specified path
    #[inline]
    fn set_path<T: Into<ContextMapValue>>(&self, path: &[&Document], doc: T) -> Result<()> {
        self.set_path_inner(path, doc.into())
    }

    /// Get the root context value
    #[inline]
    fn get(&self) -> Data {
        self.get_path_inner(&[], self)
    }

    /// Get the value at the path
    #[inline]
    fn get_path(&self, path: &[&Document]) -> Data {
        self.get_path_inner(path, self)
    }
}

#[derive(Debug)]
pub enum ContextWrapper<'a> {
    Standard(&'a StandardContext),
    #[cfg(feature = "shared-context")]
    Shared(&'a SharedContext),
    Scope(&'a ScopedContext<'a>),
}

impl<'a> Context for ContextWrapper<'a> {
    fn set_path_inner(&self, path: &[&Document], doc: ContextMapValue) -> Result<()> {
        match self {
            Self::Standard(c) => c.set_path_inner(path, doc),
            #[cfg(feature = "shared-context")]
            Self::Shared(c) => c.set_path_inner(path, doc),
            Self::Scope(c) => c.set_path_inner(path, doc),
        }
    }

    fn get_path_inner(&self, path: &[&Document], ctx: &impl Context) -> Data {
        match self {
            Self::Standard(c) => c.get_path_inner(path, ctx),
            #[cfg(feature = "shared-context")]
            Self::Shared(c) => c.get_path_inner(path, ctx),
            Self::Scope(c) => c.get_path_inner(path, ctx),
        }
    }

    fn wrap(&self) -> ContextWrapper {
        match self {
            Self::Standard(c) => c.wrap(),
            #[cfg(feature = "shared-context")]
            Self::Shared(c) => c.wrap(),
            Self::Scope(c) => c.wrap(),
        }
    }
}