#ifndef JINJA2CPP_SRC_TEMPLATE_IMPL_H #define JINJA2CPP_SRC_TEMPLATE_IMPL_H #include "internal_value.h" #include "jinja2cpp/binding/rapid_json.h" #include "jinja2cpp/template_env.h" #include "jinja2cpp/value.h" #include "renderer.h" #include "template_parser.h" #include "value_visitors.h" #include #include #include #include #include namespace jinja2 { namespace detail { template struct RapidJsonEncodingType; template<> struct RapidJsonEncodingType<1> { using type = rapidjson::UTF8; }; #ifdef BOOST_ENDIAN_BIG_BYTE template<> struct RapidJsonEncodingType<2> { using type = rapidjson::UTF16BE; }; template<> struct RapidJsonEncodingType<4> { using type = rapidjson::UTF32BE; }; #else template<> struct RapidJsonEncodingType<2> { using type = rapidjson::UTF16LE; }; template<> struct RapidJsonEncodingType<4> { using type = rapidjson::UTF32LE; }; #endif } // namespace detail extern void SetupGlobals(InternalValueMap& globalParams); class ITemplateImpl { public: virtual ~ITemplateImpl() = default; }; template struct TemplateLoader; template<> struct TemplateLoader { static auto Load(const std::string& fileName, TemplateEnv* env) { return env->LoadTemplate(fileName); } }; template<> struct TemplateLoader { static auto Load(const std::string& fileName, TemplateEnv* env) { return env->LoadTemplateW(fileName); } }; template class GenericStreamWriter : public OutStream::StreamWriter { public: explicit GenericStreamWriter(std::basic_string& os) : m_os(os) {} // StreamWriter interface void WriteBuffer(const void* ptr, size_t length) override { m_os.append(reinterpret_cast(ptr), length); } void WriteValue(const InternalValue& val) override { Apply>(val, m_os); } private: std::basic_string& m_os; }; template class StringStreamWriter : public OutStream::StreamWriter { public: explicit StringStreamWriter(std::basic_string* targetStr) : m_targetStr(targetStr) {} // StreamWriter interface void WriteBuffer(const void* ptr, size_t length) override { m_targetStr->append(reinterpret_cast(ptr), length); // m_os.write(reinterpret_cast(ptr), length); } void WriteValue(const InternalValue& val) override { Apply>(val, *m_targetStr); } private: std::basic_string* m_targetStr; }; template struct ErrorConverter; template struct ErrorConverter, ErrorInfoTpl> { static ErrorInfoTpl Convert(const ErrorInfoTpl& srcError) { typename ErrorInfoTpl::Data errorData; errorData.code = srcError.GetCode(); errorData.srcLoc = srcError.GetErrorLocation(); errorData.locationDescr = ConvertString>(srcError.GetLocationDescr()); errorData.extraParams = srcError.GetExtraParams(); return ErrorInfoTpl(errorData); } }; template struct ErrorConverter, ErrorInfoTpl> { static const ErrorInfoTpl& Convert(const ErrorInfoTpl& srcError) { return srcError; } }; template inline bool operator==(const MetadataInfo& lhs, const MetadataInfo& rhs) { if (lhs.metadata != rhs.metadata) return false; if (lhs.metadataType != rhs.metadataType) return false; if (lhs.location != rhs.location) return false; return true; } template inline bool operator!=(const MetadataInfo& lhs, const MetadataInfo& rhs) { return !(lhs == rhs); } inline bool operator==(const TemplateEnv& lhs, const TemplateEnv& rhs) { return lhs.IsEqual(rhs); } inline bool operator!=(const TemplateEnv& lhs, const TemplateEnv& rhs) { return !(lhs == rhs); } inline bool operator==(const SourceLocation& lhs, const SourceLocation& rhs) { if (lhs.fileName != rhs.fileName) return false; if (lhs.line != rhs.line) return false; if (lhs.col != rhs.col) return false; return true; } inline bool operator!=(const SourceLocation& lhs, const SourceLocation& rhs) { return !(lhs == rhs); } template class TemplateImpl : public ITemplateImpl { public: using ThisType = TemplateImpl; explicit TemplateImpl(TemplateEnv* env) : m_env(env) { if (env) m_settings = env->GetSettings(); } auto GetRenderer() const {return m_renderer;} auto GetTemplateName() const {}; boost::optional> Load(std::basic_string tpl, std::string tplName) { m_template = std::move(tpl); m_templateName = tplName.empty() ? std::string("noname.j2tpl") : std::move(tplName); TemplateParser parser(&m_template, m_settings, m_env, m_templateName); auto parseResult = parser.Parse(); if (!parseResult) return parseResult.error()[0]; m_renderer = *parseResult; m_metadataInfo = parser.GetMetadataInfo(); return boost::optional>(); } boost::optional> Render(std::basic_string& os, const ValuesMap& params) { boost::optional> normalResult; if (!m_renderer) { typename ErrorInfoTpl::Data errorData; errorData.code = ErrorCode::TemplateNotParsed; errorData.srcLoc.col = 1; errorData.srcLoc.line = 1; errorData.srcLoc.fileName = ""; return ErrorInfoTpl(errorData); } try { InternalValueMap extParams; InternalValueMap intParams; auto convertFn = [&intParams](const auto& params) { for (auto& ip : params) { auto valRef = &ip.second.data(); auto newParam = visit(visitors::InputValueConvertor(false, true), *valRef); if (!newParam) intParams[ip.first] = ValueRef(static_cast(*valRef)); else intParams[ip.first] = newParam.get(); } }; if (m_env) { m_env->ApplyGlobals(convertFn); std::swap(extParams, intParams); } convertFn(params); SetupGlobals(extParams); RendererCallback callback(this); RenderContext context(intParams, extParams, &callback); InitRenderContext(context); OutStream outStream([writer = GenericStreamWriter(os)]() mutable -> OutStream::StreamWriter* {return &writer;}); m_renderer->Render(outStream, context); } catch (const ErrorInfoTpl& error) { return ErrorConverter, ErrorInfoTpl>::Convert(error); } catch (const ErrorInfoTpl& error) { return ErrorConverter, ErrorInfoTpl>::Convert(error); } catch (const std::exception& ex) { typename ErrorInfoTpl::Data errorData; errorData.code = ErrorCode::UnexpectedException; errorData.srcLoc.col = 1; errorData.srcLoc.line = 1; errorData.srcLoc.fileName = m_templateName; errorData.extraParams.push_back(Value(std::string(ex.what()))); return ErrorInfoTpl(errorData); } return normalResult; } InternalValueMap& InitRenderContext(RenderContext& context) { auto& curScope = context.GetCurrentScope(); return curScope; } using TplLoadResultType = std::variant>, ErrorInfo>, nonstd::expected>, ErrorInfoW>>; using TplOrError = nonstd::expected>, ErrorInfoTpl>; TplLoadResultType LoadTemplate(const std::string& fileName) { if (!m_env) return TplLoadResultType(EmptyValue()); auto tplWrapper = TemplateLoader::Load(fileName, m_env); if (!tplWrapper) return TplLoadResultType(TplOrError(tplWrapper.get_unexpected())); return TplLoadResultType(TplOrError(std::static_pointer_cast(tplWrapper.value().m_impl))); } TplLoadResultType LoadTemplate(const InternalValue& fileName) { auto name = GetAsSameString(std::string(), fileName); if (!name) { typename ErrorInfoTpl::Data errorData; errorData.code = ErrorCode::InvalidTemplateName; errorData.srcLoc.col = 1; errorData.srcLoc.line = 1; errorData.srcLoc.fileName = m_templateName; errorData.extraParams.push_back(IntValue2Value(fileName)); return TplOrError(nonstd::make_unexpected(ErrorInfoTpl(errorData))); } return LoadTemplate(name.value()); } nonstd::expected> GetMetadata() const { auto& metadataString = m_metadataInfo.metadata; if (metadataString.empty()) return GenericMap(); if (m_metadataInfo.metadataType == "json") { m_metadataJson = JsonDocumentType(); rapidjson::ParseResult res = m_metadataJson.value().Parse(metadataString.data(), metadataString.size()); if (!res) { typename ErrorInfoTpl::Data errorData; errorData.code = ErrorCode::MetadataParseError; errorData.srcLoc = m_metadataInfo.location; std::string jsonError = rapidjson::GetParseError_En(res.Code()); errorData.extraParams.push_back(Value(std::move(jsonError))); return nonstd::make_unexpected(ErrorInfoTpl(errorData)); } m_metadata = std::move(std::get(Reflect(m_metadataJson.value()).data())); return m_metadata.value(); } return GenericMap(); } nonstd::expected, ErrorInfoTpl> GetMetadataRaw() const { return m_metadataInfo; } bool operator==(const TemplateImpl& other) const { if (m_env && other.m_env) { if (*m_env != *other.m_env) return false; } if (m_settings != other.m_settings) return false; if (m_template != other.m_template) return false; if (m_renderer && other.m_renderer && !m_renderer->IsEqual(*other.m_renderer)) return false; if (m_metadata != other.m_metadata) return false; if (m_metadataJson != other.m_metadataJson) return false; if (m_metadataInfo != other.m_metadataInfo) return false; return true; } private: void ThrowRuntimeError(ErrorCode code, ValuesList extraParams) { typename ErrorInfoTpl::Data errorData; errorData.code = code; errorData.srcLoc.col = 1; errorData.srcLoc.line = 1; errorData.srcLoc.fileName = m_templateName; errorData.extraParams = std::move(extraParams); throw ErrorInfoTpl(std::move(errorData)); } class RendererCallback : public IRendererCallback { public: explicit RendererCallback(ThisType* host) : m_host(host) {} TargetString GetAsTargetString(const InternalValue& val) override { std::basic_string os; Apply>(val, os); return TargetString(std::move(os)); } OutStream GetStreamOnString(TargetString& str) override { using string_t = std::basic_string; str = string_t(); return OutStream([writer = StringStreamWriter(&std::get(str))]() mutable -> OutStream::StreamWriter* { return &writer; }); } std::variant>, ErrorInfo>, nonstd::expected>, ErrorInfoW>> LoadTemplate(const std::string& fileName) const override { return m_host->LoadTemplate(fileName); } std::variant>, ErrorInfo>, nonstd::expected>, ErrorInfoW>> LoadTemplate(const InternalValue& fileName) const override { return m_host->LoadTemplate(fileName); } void ThrowRuntimeError(ErrorCode code, ValuesList extraParams) override { m_host->ThrowRuntimeError(code, std::move(extraParams)); } bool IsEqual(const IComparable& other) const override { auto* callback = dynamic_cast(&other); if (!callback) return false; if (m_host && callback->m_host) return *m_host == *(callback->m_host); if ((!m_host && (callback->m_host)) || (m_host && !(callback->m_host))) return false; return true; } bool operator==(const IComparable& other) const { auto* callback = dynamic_cast(&other); if (!callback) return false; if (m_host && callback->m_host) return *m_host == *(callback->m_host); if ((!m_host && (callback->m_host)) || (m_host && !(callback->m_host))) return false; return true; } private: ThisType* m_host; }; private: using JsonDocumentType = rapidjson::GenericDocument::type>; TemplateEnv* m_env{}; Settings m_settings; std::basic_string m_template; std::string m_templateName; RendererPtr m_renderer; mutable std::optional m_metadata; mutable std::optional m_metadataJson; MetadataInfo m_metadataInfo; }; } // namespace jinja2 #endif // JINJA2CPP_SRC_TEMPLATE_IMPL_H