13 #include "crow/json.h"
14 #include "crow/logging.h"
15 #include "crow/returnable.h"
16 #include "crow/utility.h"
42 using context = json::wvalue;
44 template_t
load(
const std::string& filename);
55 msg(
"crow::mustache error: " + msg_)
57 virtual const char* what()
const throw()
override
79 returnable(
"text/html"), body_(std::move(body)) {}
83 std::string dump()
const override
122 start(
static_cast<int>(start_)), end(
static_cast<int>(end_)), pos(
static_cast<int>(pos_)), t(t_)
137 body_(std::move(body))
144 std::string tag_name(
const Action& action)
const
146 return body_.substr(action.start, action.end - action.start);
148 auto find_context(
const std::string& name,
const std::vector<const context*>& stack,
bool shouldUseOnlyFirstStackValue =
false)
const -> std::pair<bool, const context&>
152 return {
true, *stack.back()};
157 int dotPosition = name.find(
".");
158 if (dotPosition ==
static_cast<int>(name.npos))
160 for (
auto it = stack.rbegin(); it != stack.rend(); ++it)
162 if ((*it)->t() == json::type::Object)
164 if ((*it)->count(name))
165 return {
true, (**it)[name]};
171 std::vector<int> dotPositions;
172 dotPositions.push_back(-1);
173 while (dotPosition !=
static_cast<int>(name.npos))
175 dotPositions.push_back(dotPosition);
176 dotPosition = name.find(
".", dotPosition + 1);
178 dotPositions.push_back(name.size());
179 std::vector<std::string> names;
180 names.reserve(dotPositions.size() - 1);
181 for (
int i = 1; i < static_cast<int>(dotPositions.size()); i++)
182 names.emplace_back(name.substr(dotPositions[i - 1] + 1, dotPositions[i] - dotPositions[i - 1] - 1));
184 for (
auto it = stack.rbegin(); it != stack.rend(); ++it)
188 for (
auto jt = names.begin(); jt != names.end(); ++jt)
190 if (view->t() == json::type::Object &&
193 view = &(*view)[*jt];
197 if (shouldUseOnlyFirstStackValue)
199 return {
false, empty_str};
206 return {
true, *view};
210 return {
false, empty_str};
213 void escape(
const std::string& in, std::string& out)
const
215 out.reserve(out.size() + in.size());
216 for (
auto it = in.begin(); it != in.end(); ++it)
220 case '&': out +=
"&";
break;
221 case '<': out +=
"<";
break;
222 case '>': out +=
">";
break;
223 case '"': out +=
""";
break;
224 case '\'': out +=
"'";
break;
225 case '/': out +=
"/";
break;
226 case '`': out +=
"`";
break;
227 case '=': out +=
"=";
break;
228 default: out += *it;
break;
233 bool isTagInsideObjectBlock(
const int& current,
const std::vector<const context*>& stack)
const
236 for (
int i = current; i > 0; --i)
238 auto& action = actions_[i - 1];
240 if (action.t == ActionType::OpenBlock)
242 if (openedBlock == 0 && (*stack.rbegin())->t() == json::type::Object)
248 else if (action.t == ActionType::CloseBlock)
257 void render_internal(
int actionBegin,
int actionEnd, std::vector<const context*>& stack, std::string& out,
int indent)
const
259 int current = actionBegin;
262 out.insert(out.size(), indent,
' ');
264 while (current < actionEnd)
266 auto& fragment = fragments_[current];
267 auto& action = actions_[current];
268 render_fragment(fragment, indent, out);
271 case ActionType::Ignore:
274 case ActionType::Partial:
276 std::string partial_name = tag_name(action);
277 auto partial_templ =
load(partial_name);
278 int partial_indent = action.pos;
279 partial_templ.render_internal(0, partial_templ.fragments_.size() - 1, stack, out, partial_indent ? indent + partial_indent : 0);
282 case ActionType::UnescapeTag:
283 case ActionType::Tag:
285 bool shouldUseOnlyFirstStackValue =
false;
286 if (isTagInsideObjectBlock(current, stack))
288 shouldUseOnlyFirstStackValue =
true;
290 auto optional_ctx = find_context(tag_name(action), stack, shouldUseOnlyFirstStackValue);
291 auto& ctx = optional_ctx.second;
294 case json::type::False:
295 case json::type::True:
296 case json::type::Number:
299 case json::type::String:
300 if (action.t == ActionType::Tag)
305 case json::type::Function:
307 std::string execute_result = ctx.execute();
308 while (execute_result.find(
"{{") != std::string::npos)
314 if (action.t == ActionType::Tag)
315 escape(execute_result, out);
317 out += execute_result;
321 throw std::runtime_error(
"not implemented tag type" + utility::lexical_cast<std::string>(
static_cast<int>(ctx.t())));
325 case ActionType::ElseBlock:
328 auto optional_ctx = find_context(tag_name(action), stack);
329 if (!optional_ctx.first)
331 stack.emplace_back(&nullContext);
335 auto& ctx = optional_ctx.second;
338 case json::type::List:
339 if (ctx.l && !ctx.l->empty())
340 current = action.pos;
342 stack.emplace_back(&nullContext);
344 case json::type::False:
345 case json::type::Null:
346 stack.emplace_back(&nullContext);
349 current = action.pos;
354 case ActionType::OpenBlock:
356 auto optional_ctx = find_context(tag_name(action), stack);
357 if (!optional_ctx.first)
359 current = action.pos;
363 auto& ctx = optional_ctx.second;
366 case json::type::List:
368 for (
auto it = ctx.l->begin(); it != ctx.l->end(); ++it)
370 stack.push_back(&*it);
371 render_internal(current + 1, action.pos, stack, out, indent);
374 current = action.pos;
376 case json::type::Number:
377 case json::type::String:
378 case json::type::Object:
379 case json::type::True:
380 stack.push_back(&ctx);
382 case json::type::False:
383 case json::type::Null:
384 current = action.pos;
387 throw std::runtime_error(
"{{#: not implemented context type: " + utility::lexical_cast<std::string>(
static_cast<int>(ctx.t())));
392 case ActionType::CloseBlock:
396 throw std::runtime_error(
"not implemented " + utility::lexical_cast<std::string>(
static_cast<int>(action.t)));
400 auto& fragment = fragments_[actionEnd];
401 render_fragment(fragment, indent, out);
403 void render_fragment(
const std::pair<int, int> fragment,
int indent, std::string& out)
const
407 for (
int i = fragment.first; i < fragment.second; i++)
410 if (body_[i] ==
'\n' && i + 1 !=
static_cast<int>(body_.size()))
411 out.insert(out.size(), indent,
' ');
415 out.insert(out.size(), body_, fragment.first, fragment.second - fragment.first);
423 std::vector<const context*> stack;
424 stack.emplace_back(&empty_ctx);
427 render_internal(0, fragments_.size() - 1, stack, ret, 0);
434 std::vector<const context*> stack;
435 stack.emplace_back(&ctx);
438 render_internal(0, fragments_.size() - 1, stack, ret, 0);
452 std::vector<const context*> stack;
453 stack.emplace_back(&empty_ctx);
456 render_internal(0, fragments_.size() - 1, stack, ret, 0);
463 std::vector<const context*> stack;
464 stack.emplace_back(&ctx);
467 render_internal(0, fragments_.size() - 1, stack, ret, 0);
474 std::string tag_open =
"{{";
475 std::string tag_close =
"}}";
477 std::vector<int> blockPositions;
482 size_t idx = body_.find(tag_open, current);
483 if (idx == body_.npos)
485 fragments_.emplace_back(
static_cast<int>(current),
static_cast<int>(body_.size()));
486 actions_.emplace_back(ActionType::Ignore, 0, 0);
489 fragments_.emplace_back(
static_cast<int>(current),
static_cast<int>(idx));
491 idx += tag_open.size();
492 size_t endIdx = body_.find(tag_close, idx);
495 throw invalid_template_exception(
"empty tag is not allowed");
497 if (endIdx == body_.npos)
500 throw invalid_template_exception(
"not matched opening tag");
502 current = endIdx + tag_close.size();
507 while (body_[idx] ==
' ')
509 while (body_[endIdx - 1] ==
' ')
511 blockPositions.emplace_back(
static_cast<int>(actions_.size()));
512 actions_.emplace_back(ActionType::OpenBlock, idx, endIdx);
516 while (body_[idx] ==
' ')
518 while (body_[endIdx - 1] ==
' ')
521 auto& matched = actions_[blockPositions.back()];
522 if (body_.compare(idx, endIdx - idx,
523 body_, matched.start, matched.end - matched.start) != 0)
525 throw invalid_template_exception(
"not matched {{# {{/ pair: " +
526 body_.substr(matched.start, matched.end - matched.start) +
", " +
527 body_.substr(idx, endIdx - idx));
529 matched.pos = actions_.size();
531 actions_.emplace_back(ActionType::CloseBlock, idx, endIdx, blockPositions.back());
532 blockPositions.pop_back();
536 while (body_[idx] ==
' ')
538 while (body_[endIdx - 1] ==
' ')
540 blockPositions.emplace_back(
static_cast<int>(actions_.size()));
541 actions_.emplace_back(ActionType::ElseBlock, idx, endIdx);
545 actions_.emplace_back(ActionType::Ignore, idx + 1, endIdx);
549 while (body_[idx] ==
' ')
551 while (body_[endIdx - 1] ==
' ')
553 actions_.emplace_back(ActionType::Partial, idx, endIdx);
556 if (tag_open !=
"{{" || tag_close !=
"}}")
557 throw invalid_template_exception(
"cannot use triple mustache when delimiter changed");
560 if (body_[endIdx + 2] !=
'}')
562 throw invalid_template_exception(
"{{{: }}} not matched");
564 while (body_[idx] ==
' ')
566 while (body_[endIdx - 1] ==
' ')
568 actions_.emplace_back(ActionType::UnescapeTag, idx, endIdx);
573 while (body_[idx] ==
' ')
575 while (body_[endIdx - 1] ==
' ')
577 actions_.emplace_back(ActionType::UnescapeTag, idx, endIdx);
582 actions_.emplace_back(ActionType::Ignore, idx, endIdx);
584 if (body_[endIdx] !=
'=')
585 throw invalid_template_exception(
"{{=: not matching = tag: " + body_.substr(idx, endIdx - idx));
587 while (body_[idx] ==
' ')
589 while (body_[endIdx] ==
' ')
593 bool succeeded =
false;
594 for (
size_t i = idx; i < endIdx; i++)
598 tag_open = body_.substr(idx, i - idx);
599 while (body_[i] ==
' ')
601 tag_close = body_.substr(i, endIdx - i);
602 if (tag_open.empty())
603 throw invalid_template_exception(
"{{=: empty open tag");
604 if (tag_close.empty())
605 throw invalid_template_exception(
"{{=: empty close tag");
607 if (tag_close.find(
" ") != tag_close.npos)
608 throw invalid_template_exception(
"{{=: invalid open/close tag: " + tag_open +
" " + tag_close);
614 throw invalid_template_exception(
"{{=: cannot find space between new open/close tags");
619 while (body_[idx] ==
' ')
621 while (body_[endIdx - 1] ==
' ')
623 actions_.emplace_back(ActionType::Tag, idx, endIdx);
629 for (
int i = actions_.size() - 2; i >= 0; i--)
631 if (actions_[i].t == ActionType::Tag || actions_[i].t == ActionType::UnescapeTag)
633 auto& fragment_before = fragments_[i];
634 auto& fragment_after = fragments_[i + 1];
635 bool is_last_action = i ==
static_cast<int>(actions_.size()) - 2;
636 bool all_space_before =
true;
638 for (j = fragment_before.second - 1; j >= fragment_before.first; j--)
642 all_space_before =
false;
646 if (all_space_before && i > 0)
648 if (!all_space_before && body_[j] !=
'\n')
650 bool all_space_after =
true;
651 for (k = fragment_after.first; k <
static_cast<int>(body_.size()) && k < fragment_after.second; k++)
655 all_space_after =
false;
659 if (all_space_after && !is_last_action)
661 if (!all_space_after &&
665 k + 1 <
static_cast<int>(body_.size()) &&
666 body_[k + 1] ==
'\n')))
668 if (actions_[i].t == ActionType::Partial)
670 actions_[i].pos = fragment_before.second - j - 1;
672 fragment_before.second = j + 1;
673 if (!all_space_after)
675 if (body_[k] ==
'\n')
679 fragment_after.first = k;
684 std::vector<std::pair<int, int>> fragments_;
685 std::vector<Action> actions_;
698 inline std::string& get_template_base_directory_ref()
700 static std::string template_base_directory =
"templates";
701 return template_base_directory;
707 static std::string template_base_directory =
"templates";
708 return template_base_directory;
717 std::string path = detail::get_template_base_directory_ref();
718 std::ifstream inf(utility::join_path(path, filename));
721 CROW_LOG_WARNING <<
"Template \"" << filename <<
"\" not found.";
724 return {std::istreambuf_iterator<char>(inf), std::istreambuf_iterator<char>()};
729 inline std::function<std::string(std::string)>& get_loader_ref()
731 static std::function<std::string(std::string)> loader =
default_loader;
740 auto& base = detail::get_template_base_directory_ref();
742 if (base.back() !=
'\\' &&
753 auto& base = detail::get_global_template_base_directory_ref();
755 if (base.back() !=
'\\' &&
768 inline void set_loader(std::function<std::string(std::string)> loader)
770 detail::get_loader_ref() = std::move(loader);
778 inline std::string
load_text(
const std::string& filename)
780 std::string filename_sanitized(filename);
781 utility::sanitize_filename(filename_sanitized);
782 return detail::get_loader_ref()(filename_sanitized);
807 return detail::get_loader_ref()(filename);
814 std::string filename_sanitized(filename);
815 utility::sanitize_filename(filename_sanitized);
816 return compile(detail::get_loader_ref()(filename_sanitized));
828 return compile(detail::get_loader_ref()(filename));
JSON write value.
Definition: json.h:1291
Represents compilation error of an template. Throwed specially at mustache compile time.
Definition: mustache.h:52
Compiled mustache template object.
Definition: mustache.h:134
std::string render_string() const
Output a returnable template from this mustache template.
Definition: mustache.h:449
rendered_template render() const
Output a returnable template from this mustache template.
Definition: mustache.h:420
rendered_template render(const context &ctx) const
Apply the values from the context provided and output a returnable template from this mustache templa...
Definition: mustache.h:432
rendered_template render(const context &&ctx) const
Apply the values from the context provided and output a returnable template from this mustache templa...
Definition: mustache.h:443
std::string render_string(const context &ctx) const
Apply the values from the context provided and output a returnable template from this mustache templa...
Definition: mustache.h:461
std::string & get_global_template_base_directory_ref()
A base directory not related to any blueprint.
Definition: mustache.h:705
void set_loader(std::function< std::string(std::string)> loader)
Change the way that load, load_unsafe, load_text and load_text_unsafe reads a file.
Definition: mustache.h:768
template_t compile(const std::string &body)
The function that compiles a source into a mustache template.
Definition: mustache.h:691
std::string load_text_unsafe(const std::string &filename)
Open and read a file but returns a std::string without a previous rendering process.
Definition: mustache.h:805
void set_global_base(const std::string &path)
Defines the templates directory path at global level. By default is templates/.
Definition: mustache.h:751
std::string default_loader(const std::string &filename)
The default way that load, load_unsafe, load_text and load_text_unsafe use to read the contents of a ...
Definition: mustache.h:715
void set_base(const std::string &path)
Defines the templates directory path at route level. By default is templates/.
Definition: mustache.h:738
std::string load_text(const std::string &filename)
Open, read and sanitize a file but returns a std::string without a previous rendering process.
Definition: mustache.h:778
template_t load(const std::string &filename)
Open, read and renders a file using a mustache compiler. It also sanitize the input before compilatio...
Definition: mustache.h:812
ActionType
Used in Action to represent different parsing behaviors.
Definition: mustache.h:97
template_t load_unsafe(const std::string &filename)
Open, read and renders a file using a mustache compiler. But it do not sanitize the input before comp...
Definition: mustache.h:826
The main namespace of the library. In this namespace is defined the most important classes and functi...
Used during mustache template compilation to represent parsing actions.
Definition: mustache.h:116
Returned object after call the template_t::render() method. Its intended to be returned during a rule...
Definition: mustache.h:74
An abstract class that allows any other class to be returned by a handler.
Definition: returnable.h:9