diff options
author | ipknHama <ipknhama@gmail.com> | 2014-08-02 10:46:00 +0900 |
---|---|---|
committer | ipknHama <ipknhama@gmail.com> | 2014-08-02 22:32:49 +0900 |
commit | 615e648260a87eb2edabf4aefb456186800e67a1 (patch) | |
tree | 7bd8b268a960b2b7d322407160a9d152e32a9251 /mustache.h | |
parent | 9d1d65b08c4a4c33923935a5414d6e6f4fe59ceb (diff) | |
download | crow-615e648260a87eb2edabf4aefb456186800e67a1.tar.gz crow-615e648260a87eb2edabf4aefb456186800e67a1.zip |
mustache partial implementation
Diffstat (limited to 'mustache.h')
-rw-r--r-- | mustache.h | 83 |
1 files changed, 77 insertions, 6 deletions
@@ -1,6 +1,9 @@ #pragma once #include <string> #include <vector> +#include <fstream> +#include <iterator> +#include <functional> #include "json.h" namespace crow { @@ -8,6 +11,8 @@ namespace crow { using context = json::wvalue; + template_t load(const std::string& filename); + class invalid_template_exception : public std::exception { public: @@ -138,19 +143,31 @@ namespace crow } } - void render_internal(int actionBegin, int actionEnd, std::vector<context*>& stack, std::string& out) + void render_internal(int actionBegin, int actionEnd, std::vector<context*>& stack, std::string& out, int indent) { int current = actionBegin; + + if (indent) + out.insert(out.size(), indent, ' '); + while(current < actionEnd) { auto& fragment = fragments_[current]; auto& action = actions_[current]; - out.insert(out.size(), body_, fragment.first, fragment.second-fragment.first); + render_fragment(fragment, indent, out); switch(action.t) { case ActionType::Ignore: // do nothing break; + case ActionType::Partial: + { + std::string partial_name = tag_name(action); + auto partial_templ = load(partial_name); + int partial_indent = action.pos; + partial_templ.render_internal(0, partial_templ.fragments_.size()-1, stack, out, partial_indent?indent+partial_indent:0); + } + break; case ActionType::UnescapeTag: case ActionType::Tag: { @@ -218,7 +235,7 @@ namespace crow for(auto it = ctx.l->begin(); it != ctx.l->end(); ++it) { stack.push_back(&*it); - render_internal(current+1, action.pos, stack, out); + render_internal(current+1, action.pos, stack, out, indent); stack.pop_back(); } current = action.pos; @@ -248,7 +265,21 @@ namespace crow current++; } auto& fragment = fragments_[actionEnd]; - out.insert(out.size(), body_, fragment.first, fragment.second - fragment.first); + render_fragment(fragment, indent, out); + } + void render_fragment(const std::pair<int, int> fragment, int indent, std::string& out) + { + if (indent) + { + for(int i = fragment.first; i < fragment.second; i ++) + { + out += body_[i]; + if (body_[i] == '\n' && i+1 != (int)body_.size()) + out.insert(out.size(), indent, ' '); + } + } + else + out.insert(out.size(), body_, fragment.first, fragment.second-fragment.first); } public: std::string render(context& ctx) @@ -257,7 +288,7 @@ namespace crow stack.emplace_back(&ctx); std::string ret; - render_internal(0, fragments_.size()-1, stack, ret); + render_internal(0, fragments_.size()-1, stack, ret, 0); return ret; } @@ -337,7 +368,6 @@ namespace crow while(body_[idx] == ' ') idx++; while(body_[endIdx-1] == ' ') endIdx--; actions_.emplace_back(ActionType::Partial, idx, endIdx); - throw invalid_template_exception("{{>: partial not implemented: " + body_.substr(idx+1, endIdx-idx-1)); break; case '{': if (tag_open != "{{" || tag_close != "}}") @@ -444,6 +474,10 @@ namespace crow k + 1 < (int)body_.size() && body_[k+1] == '\n'))) continue; + if (actions_[i].t == ActionType::Partial) + { + actions_[i].pos = fragment_before.second - j - 1; + } fragment_before.second = j+1; if (!all_space_after) { @@ -465,5 +499,42 @@ namespace crow { return template_t(body); } + namespace detail + { + std::string template_base_directory = "templates"; + } + + std::string default_loader(const std::string& filename) + { + std::ifstream inf(detail::template_base_directory + filename); + if (!inf) + return {}; + return {std::istreambuf_iterator<char>(inf), std::istreambuf_iterator<char>()}; + } + + namespace detail + { + std::function<std::string (std::string)> loader = default_loader; + } + + void set_base(const std::string& path) + { + detail::template_base_directory = path; + if (detail::template_base_directory.back() != '\\' && + detail::template_base_directory.back() != '/') + { + detail::template_base_directory += '/'; + } + } + + void set_loader(std::function<std::string(std::string)> loader) + { + detail::loader = std::move(loader); + } + + template_t load(const std::string& filename) + { + return compile(detail::loader(filename)); + } } } |