#include #include #include #include #include namespace nntpchan { template struct overloaded : Ts... { using Ts::operator()...; }; template overloaded(Ts...) -> overloaded; namespace mustache = mstch; static mustache::map post_to_map(const nntpchan::model::Post & post) { mustache::map m; mustache::array attachments; mustache::map h; for (const auto & att : nntpchan::model::GetAttachments(post)) { mustache::map a; a["filename"] = nntpchan::model::GetFilename(att); a["hexdigest"] = nntpchan::model::GetHexDigest(att); a["thumbnail"] = nntpchan::model::GetThumbnail(att); attachments.push_back(a); } for (const auto & item : nntpchan::model::GetHeader(post)) { mustache::array vals; for(const auto & v : item.second) vals.push_back(v); h[item.first] = vals; } m["attachments"] = attachments; m["message"] = nntpchan::model::GetBody(post); m["header"] = h; return m; } static mustache::map thread_to_map(const nntpchan::model::Thread & t) { mustache::map thread; mustache::array posts; for(const auto & post : t) { posts.push_back(post_to_map(post)); } auto & opHeader = nntpchan::model::GetHeader(t[0]); thread["title"] = nntpchan::model::HeaderIFind(opHeader, "subject", "None")[0]; thread["posts"] = posts; return thread; } struct MustacheTemplateEngine : public TemplateEngine { struct Impl { Impl(const std::map & partials) : m_partials(partials) {} bool ParseTemplate(const FileHandle_ptr & in) { std::stringstream str; std::string line; while(std::getline(*in, line)) str << line << "\n"; m_tmplString = str.str(); return in->eof(); } bool RenderFile(const Args_t & args, const FileHandle_ptr & out) { mustache::map obj; for (const auto & item : args) { std::visit(overloaded { [&obj, item](const nntpchan::model::Model & m) { std::visit(overloaded { [&obj, item](const nntpchan::model::BoardPage & p) { mustache::array threads; for (const auto & thread : p) { threads.push_back(thread_to_map(thread)); } obj[item.first] = threads; }, [&obj, item](const nntpchan::model::Thread & t) { obj[item.first] = thread_to_map(t); } }, m); } ,[&obj, item](const std::string & str) { obj[item.first] = str; } }, item.second); } std::string str = mustache::render(m_tmplString, obj); out->write(str.c_str(), str.size()); out->flush(); return !out->fail(); } std::string m_tmplString; const std::map & m_partials; }; virtual bool WriteTemplate(const fs::path & fpath, const Args_t & args, const FileHandle_ptr & out) { auto templFile = OpenFile(fpath, eRead); if(!templFile) { std::clog << "no such template at " << fpath << std::endl; return false; } std::map partials; if(!LoadPartials(fpath.parent_path(), partials)) { std::clog << "failed to load partials" << std::endl; return false; } Impl impl(partials); if(impl.ParseTemplate(templFile)) { return impl.RenderFile(args, out); } std::clog << "failed to parse template " << fpath << std::endl; return false; } bool LoadPartials(fs::path dir, std::map & partials) { const auto partial_files = { "header", "footer" }; for(const auto & fname : partial_files) { auto file = OpenFile(dir / fs::path(fname + std::string(".html")), eRead); if(!file) { std::clog << "no such partial: " << fname << std::endl; return false; } std::string line; std::stringstream input; while(std::getline(*file, line)) input << line << "\n"; partials[fname] = input.str(); } return true; } }; TemplateEngine * CreateTemplateEngine(const std::string & dialect) { auto d = ToLower(dialect); if(d == "mustache") return new MustacheTemplateEngine; else return nullptr; } }