#ifndef JINJA2CPP_SRC_RENDER_CONTEXT_H
#define JINJA2CPP_SRC_RENDER_CONTEXT_H
#include "internal_value.h"
#include <jinja2cpp/error_info.h>
#include <jinja2cpp/utils/i_comparable.h>
#include <contrib/restricted/expected-lite/include/nonstd/expected.hpp>
#include <list>
#include <deque>
namespace jinja2
{
template<typename CharT>
class TemplateImpl;
struct IRendererCallback : IComparable
{
virtual ~IRendererCallback() {}
virtual TargetString GetAsTargetString(const InternalValue& val) = 0;
virtual OutStream GetStreamOnString(TargetString& str) = 0;
virtual std::variant<EmptyValue,
nonstd::expected<std::shared_ptr<TemplateImpl<char>>, ErrorInfo>,
nonstd::expected<std::shared_ptr<TemplateImpl<wchar_t>>, ErrorInfoW>> LoadTemplate(const std::string& fileName) const = 0;
virtual std::variant<EmptyValue,
nonstd::expected<std::shared_ptr<TemplateImpl<char>>, ErrorInfo>,
nonstd::expected<std::shared_ptr<TemplateImpl<wchar_t>>, ErrorInfoW>> LoadTemplate(const InternalValue& fileName) const = 0;
virtual void ThrowRuntimeError(ErrorCode code, ValuesList extraParams) = 0;
};
class RenderContext
{
public:
RenderContext(const InternalValueMap& extValues, const InternalValueMap& globalValues, IRendererCallback* rendererCallback)
: m_rendererCallback(rendererCallback)
{
m_externalScope = &extValues;
m_globalScope = &globalValues;
EnterScope();
(*m_currentScope)["self"] = CreateMapAdapter(InternalValueMap());
}
RenderContext(const RenderContext& other)
: m_rendererCallback(other.m_rendererCallback)
, m_externalScope(other.m_externalScope)
, m_globalScope(other.m_globalScope)
, m_boundScope(other.m_boundScope)
, m_scopes(other.m_scopes)
{
m_currentScope = &m_scopes.back();
}
InternalValueMap& EnterScope()
{
m_scopes.push_back(InternalValueMap());
m_currentScope = &m_scopes.back();
return *m_currentScope;
}
void ExitScope()
{
m_scopes.pop_back();
if (!m_scopes.empty())
m_currentScope = &m_scopes.back();
else
m_currentScope = nullptr;
}
auto FindValue(const std::string& val, bool& found) const
{
auto finder = [&val, &found](auto& map) mutable
{
auto p = map.find(val);
if (p != map.end())
found = true;
return p;
};
if (m_boundScope)
{
auto valP = finder(*m_boundScope);
if (found)
return valP;
}
for (auto p = m_scopes.rbegin(); p != m_scopes.rend(); ++ p)
{
auto valP = finder(*p);
if (found)
return valP;
}
auto valP = finder(*m_externalScope);
if (found)
return valP;
return finder(*m_globalScope);
}
auto& GetCurrentScope() const
{
return *m_currentScope;
}
auto& GetCurrentScope()
{
return *m_currentScope;
}
auto& GetGlobalScope()
{
return m_scopes.front();
}
auto GetRendererCallback()
{
return m_rendererCallback;
}
RenderContext Clone(bool includeCurrentContext) const
{
if (!includeCurrentContext)
return RenderContext(m_emptyScope, *m_globalScope, m_rendererCallback);
return RenderContext(*this);
}
void BindScope(InternalValueMap* scope)
{
m_boundScope = scope;
}
bool IsEqual(const RenderContext& other) const
{
if (!IsEqual(m_rendererCallback, other.m_rendererCallback))
return false;
if (!IsEqual(this->m_currentScope, other.m_currentScope))
return false;
if (!IsEqual(m_externalScope, other.m_externalScope))
return false;
if (!IsEqual(m_globalScope, other.m_globalScope))
return false;
if (!IsEqual(m_boundScope, other.m_boundScope))
return false;
if (m_emptyScope != other.m_emptyScope)
return false;
if (m_scopes != other.m_scopes)
return false;
return true;
}
private:
bool IsEqual(const IRendererCallback* lhs, const IRendererCallback* rhs) const
{
if (lhs && rhs)
return lhs->IsEqual(*rhs);
if ((!lhs && rhs) || (lhs && !rhs))
return false;
return true;
}
bool IsEqual(const InternalValueMap* lhs, const InternalValueMap* rhs) const
{
if (lhs && rhs)
return *lhs == *rhs;
if ((!lhs && rhs) || (lhs && !rhs))
return false;
return true;
}
private:
IRendererCallback* m_rendererCallback{};
InternalValueMap* m_currentScope{};
const InternalValueMap* m_externalScope{};
const InternalValueMap* m_globalScope{};
const InternalValueMap* m_boundScope{};
InternalValueMap m_emptyScope;
std::deque<InternalValueMap> m_scopes;
};
} // namespace jinja2
#endif // JINJA2CPP_SRC_RENDER_CONTEXT_H