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
124 Action(
char tag_char_,
ActionType t_,
size_t start_,
size_t end_,
size_t pos_ = 0):
125 has_end_match(
false), tag_char(tag_char_), start(
static_cast<int>(start_)), end(
static_cast<int>(end_)), pos(
static_cast<int>(pos_)), t(t_)
129 bool missing_end_pair()
const {
132 case ActionType::Ignore:
133 case ActionType::Tag:
134 case ActionType::UnescapeTag:
135 case ActionType::CloseBlock:
136 case ActionType::Partial:
140 case ActionType::OpenBlock:
141 case ActionType::ElseBlock:
142 return !has_end_match;
145 throw std::logic_error(
"invalid type");
160 body_(std::move(body))
167 std::string tag_name(
const Action& action)
const
169 return body_.substr(action.start, action.end - action.start);
171 auto find_context(
const std::string& name,
const std::vector<const context*>& stack,
bool shouldUseOnlyFirstStackValue =
false)
const -> std::pair<bool, const context&>
175 return {
true, *stack.back()};
180 int dotPosition = name.find(
".");
181 if (dotPosition ==
static_cast<int>(name.npos))
183 for (
auto it = stack.rbegin(); it != stack.rend(); ++it)
185 if ((*it)->t() == json::type::Object)
187 if ((*it)->count(name))
188 return {
true, (**it)[name]};
194 std::vector<int> dotPositions;
195 dotPositions.push_back(-1);
196 while (dotPosition !=
static_cast<int>(name.npos))
198 dotPositions.push_back(dotPosition);
199 dotPosition = name.find(
".", dotPosition + 1);
201 dotPositions.push_back(name.size());
202 std::vector<std::string> names;
203 names.reserve(dotPositions.size() - 1);
204 for (
int i = 1; i < static_cast<int>(dotPositions.size()); i++)
205 names.emplace_back(name.substr(dotPositions[i - 1] + 1, dotPositions[i] - dotPositions[i - 1] - 1));
207 for (
auto it = stack.rbegin(); it != stack.rend(); ++it)
211 for (
auto jt = names.begin(); jt != names.end(); ++jt)
213 if (view->t() == json::type::Object &&
216 view = &(*view)[*jt];
220 if (shouldUseOnlyFirstStackValue)
222 return {
false, empty_str};
229 return {
true, *view};
233 return {
false, empty_str};
236 void escape(
const std::string& in, std::string& out)
const
238 out.reserve(out.size() + in.size());
239 for (
auto it = in.begin(); it != in.end(); ++it)
243 case '&': out +=
"&";
break;
244 case '<': out +=
"<";
break;
245 case '>': out +=
">";
break;
246 case '"': out +=
""";
break;
247 case '\'': out +=
"'";
break;
248 case '/': out +=
"/";
break;
249 case '`': out +=
"`";
break;
250 case '=': out +=
"=";
break;
251 default: out += *it;
break;
256 bool isTagInsideObjectBlock(
const int& current,
const std::vector<const context*>& stack)
const
259 for (
int i = current; i > 0; --i)
261 auto& action = actions_[i - 1];
263 if (action.t == ActionType::OpenBlock)
265 if (openedBlock == 0 && (*stack.rbegin())->t() == json::type::Object)
271 else if (action.t == ActionType::CloseBlock)
280 void render_internal(
int actionBegin,
int actionEnd, std::vector<const context*>& stack, std::string& out,
int indent)
const
282 int current = actionBegin;
285 out.insert(out.size(), indent,
' ');
287 while (current < actionEnd)
289 auto& fragment = fragments_[current];
290 auto& action = actions_[current];
291 render_fragment(fragment, indent, out);
294 case ActionType::Ignore:
297 case ActionType::Partial:
299 std::string partial_name = tag_name(action);
300 auto partial_templ =
load(partial_name);
301 int partial_indent = action.pos;
302 partial_templ.render_internal(0, partial_templ.fragments_.size() - 1, stack, out, partial_indent ? indent + partial_indent : 0);
305 case ActionType::UnescapeTag:
306 case ActionType::Tag:
308 bool shouldUseOnlyFirstStackValue =
false;
309 if (isTagInsideObjectBlock(current, stack))
311 shouldUseOnlyFirstStackValue =
true;
313 auto optional_ctx = find_context(tag_name(action), stack, shouldUseOnlyFirstStackValue);
314 auto& ctx = optional_ctx.second;
317 case json::type::False:
318 case json::type::True:
319 case json::type::Number:
322 case json::type::String:
323 if (action.t == ActionType::Tag)
328 case json::type::Function:
330 std::string execute_result = ctx.execute();
331 while (execute_result.find(
"{{") != std::string::npos)
337 if (action.t == ActionType::Tag)
338 escape(execute_result, out);
340 out += execute_result;
344 throw std::runtime_error(
"not implemented tag type" + utility::lexical_cast<std::string>(
static_cast<int>(ctx.t())));
348 case ActionType::ElseBlock:
351 auto optional_ctx = find_context(tag_name(action), stack);
352 if (!optional_ctx.first)
354 stack.emplace_back(&nullContext);
358 auto& ctx = optional_ctx.second;
361 case json::type::List:
362 if (ctx.l && !ctx.l->empty())
363 current = action.pos;
365 stack.emplace_back(&nullContext);
367 case json::type::False:
368 case json::type::Null:
369 stack.emplace_back(&nullContext);
372 current = action.pos;
377 case ActionType::OpenBlock:
379 auto optional_ctx = find_context(tag_name(action), stack);
380 if (!optional_ctx.first)
382 current = action.pos;
386 auto& ctx = optional_ctx.second;
389 case json::type::List:
391 for (
auto it = ctx.l->begin(); it != ctx.l->end(); ++it)
393 stack.push_back(&*it);
394 render_internal(current + 1, action.pos, stack, out, indent);
397 current = action.pos;
399 case json::type::Number:
400 case json::type::String:
401 case json::type::Object:
402 case json::type::True:
403 stack.push_back(&ctx);
405 case json::type::False:
406 case json::type::Null:
407 current = action.pos;
410 throw std::runtime_error(
"{{#: not implemented context type: " + utility::lexical_cast<std::string>(
static_cast<int>(ctx.t())));
415 case ActionType::CloseBlock:
419 throw std::runtime_error(
"not implemented " + utility::lexical_cast<std::string>(
static_cast<int>(action.t)));
423 auto& fragment = fragments_[actionEnd];
424 render_fragment(fragment, indent, out);
426 void render_fragment(
const std::pair<int, int> fragment,
int indent, std::string& out)
const
430 for (
int i = fragment.first; i < fragment.second; i++)
433 if (body_[i] ==
'\n' && i + 1 !=
static_cast<int>(body_.size()))
434 out.insert(out.size(), indent,
' ');
438 out.insert(out.size(), body_, fragment.first, fragment.second - fragment.first);
446 std::vector<const context*> stack;
447 stack.emplace_back(&empty_ctx);
450 render_internal(0, fragments_.size() - 1, stack, ret, 0);
457 std::vector<const context*> stack;
458 stack.emplace_back(&ctx);
461 render_internal(0, fragments_.size() - 1, stack, ret, 0);
475 std::vector<const context*> stack;
476 stack.emplace_back(&empty_ctx);
479 render_internal(0, fragments_.size() - 1, stack, ret, 0);
486 std::vector<const context*> stack;
487 stack.emplace_back(&ctx);
490 render_internal(0, fragments_.size() - 1, stack, ret, 0);
497 std::string tag_open =
"{{";
498 std::string tag_close =
"}}";
500 std::vector<int> blockPositions;
505 size_t idx = body_.find(tag_open, current);
506 if (idx == body_.npos)
508 fragments_.emplace_back(
static_cast<int>(current),
static_cast<int>(body_.size()));
509 actions_.emplace_back(
'!', ActionType::Ignore, 0, 0);
512 fragments_.emplace_back(
static_cast<int>(current),
static_cast<int>(idx));
514 idx += tag_open.size();
515 size_t endIdx = body_.find(tag_close, idx);
518 throw invalid_template_exception(
"empty tag is not allowed");
520 if (endIdx == body_.npos)
523 throw invalid_template_exception(
"not matched opening tag");
525 current = endIdx + tag_close.size();
526 char tag_char = body_[idx];
531 while (body_[idx] ==
' ')
533 while (body_[endIdx - 1] ==
' ')
535 blockPositions.emplace_back(
static_cast<int>(actions_.size()));
536 actions_.emplace_back(tag_char, ActionType::OpenBlock, idx, endIdx);
540 while (body_[idx] ==
' ')
542 while (body_[endIdx - 1] ==
' ')
545 auto& matched = actions_[blockPositions.back()];
546 if (body_.compare(idx, endIdx - idx,
547 body_, matched.start, matched.end - matched.start) != 0)
549 throw invalid_template_exception(
550 std::string(
"not matched {{")
553 + body_.substr(matched.start, matched.end - matched.start) +
", "
554 + body_.substr(idx, endIdx - idx)
557 matched.pos =
static_cast<int>(actions_.size());
558 matched.has_end_match =
true;
560 actions_.emplace_back(tag_char, ActionType::CloseBlock, idx, endIdx, blockPositions.back());
561 blockPositions.pop_back();
565 while (body_[idx] ==
' ')
567 while (body_[endIdx - 1] ==
' ')
569 blockPositions.emplace_back(
static_cast<int>(actions_.size()));
570 actions_.emplace_back(tag_char, ActionType::ElseBlock, idx, endIdx);
574 actions_.emplace_back(tag_char, ActionType::Ignore, idx + 1, endIdx);
578 while (body_[idx] ==
' ')
580 while (body_[endIdx - 1] ==
' ')
582 actions_.emplace_back(tag_char, ActionType::Partial, idx, endIdx);
585 if (tag_open !=
"{{" || tag_close !=
"}}")
586 throw invalid_template_exception(
"cannot use triple mustache when delimiter changed");
589 if (body_[endIdx + 2] !=
'}')
591 throw invalid_template_exception(
"{{{: }}} not matched");
593 while (body_[idx] ==
' ')
595 while (body_[endIdx - 1] ==
' ')
597 actions_.emplace_back(tag_char, ActionType::UnescapeTag, idx, endIdx);
602 while (body_[idx] ==
' ')
604 while (body_[endIdx - 1] ==
' ')
606 actions_.emplace_back(tag_char, ActionType::UnescapeTag, idx, endIdx);
611 actions_.emplace_back(tag_char, ActionType::Ignore, idx, endIdx);
613 if (body_[endIdx] !=
'=')
614 throw invalid_template_exception(
"{{=: not matching = tag: " + body_.substr(idx, endIdx - idx));
616 while (body_[idx] ==
' ')
618 while (body_[endIdx] ==
' ')
622 bool succeeded =
false;
623 for (
size_t i = idx; i < endIdx; i++)
627 tag_open = body_.substr(idx, i - idx);
628 while (body_[i] ==
' ')
630 tag_close = body_.substr(i, endIdx - i);
631 if (tag_open.empty())
632 throw invalid_template_exception(
"{{=: empty open tag");
633 if (tag_close.empty())
634 throw invalid_template_exception(
"{{=: empty close tag");
636 if (tag_close.find(
" ") != tag_close.npos)
637 throw invalid_template_exception(
"{{=: invalid open/close tag: " + tag_open +
" " + tag_close);
643 throw invalid_template_exception(
"{{=: cannot find space between new open/close tags");
648 while (body_[idx] ==
' ')
650 while (body_[endIdx - 1] ==
' ')
652 actions_.emplace_back(tag_char, ActionType::Tag, idx, endIdx);
658 for (
int i = 0; i < static_cast<int>(actions_.size()); i++)
660 if (actions_[i].missing_end_pair())
662 throw invalid_template_exception(
663 std::string(
"open tag has no matching end tag {{")
664 + actions_[i].tag_char
666 + body_.substr(actions_[i].start, actions_[i].end - actions_[i].start)
672 for (
int i =
static_cast<int>(actions_.size()) - 2; i >= 0; i--)
674 if (actions_[i].t == ActionType::Tag || actions_[i].t == ActionType::UnescapeTag)
676 auto& fragment_before = fragments_[i];
677 auto& fragment_after = fragments_[i + 1];
678 bool is_last_action = i ==
static_cast<int>(actions_.size()) - 2;
679 bool all_space_before =
true;
681 for (j = fragment_before.second - 1; j >= fragment_before.first; j--)
685 all_space_before =
false;
689 if (all_space_before && i > 0)
691 if (!all_space_before && body_[j] !=
'\n')
693 bool all_space_after =
true;
694 for (k = fragment_after.first; k <
static_cast<int>(body_.size()) && k < fragment_after.second; k++)
698 all_space_after =
false;
702 if (all_space_after && !is_last_action)
704 if (!all_space_after &&
708 k + 1 <
static_cast<int>(body_.size()) &&
709 body_[k + 1] ==
'\n')))
711 if (actions_[i].t == ActionType::Partial)
713 actions_[i].pos = fragment_before.second - j - 1;
715 fragment_before.second = j + 1;
716 if (!all_space_after)
718 if (body_[k] ==
'\n')
722 fragment_after.first = k;
727 std::vector<std::pair<int, int>> fragments_;
728 std::vector<Action> actions_;
741 inline std::string& get_template_base_directory_ref()
743 static std::string template_base_directory =
"templates";
744 return template_base_directory;
750 static std::string template_base_directory =
"templates";
751 return template_base_directory;
760 std::string path = detail::get_template_base_directory_ref();
761 std::ifstream inf(utility::join_path(path, filename));
764 CROW_LOG_WARNING <<
"Template \"" << filename <<
"\" not found.";
767 return {std::istreambuf_iterator<char>(inf), std::istreambuf_iterator<char>()};
772 inline std::function<std::string(std::string)>& get_loader_ref()
774 static std::function<std::string(std::string)> loader =
default_loader;
783 auto& base = detail::get_template_base_directory_ref();
785 if (base.back() !=
'\\' &&
796 auto& base = detail::get_global_template_base_directory_ref();
798 if (base.back() !=
'\\' &&
811 inline void set_loader(std::function<std::string(std::string)> loader)
813 detail::get_loader_ref() = std::move(loader);
821 inline std::string
load_text(
const std::string& filename)
823 std::string filename_sanitized(filename);
824 utility::sanitize_filename(filename_sanitized);
825 return detail::get_loader_ref()(filename_sanitized);
850 return detail::get_loader_ref()(filename);
857 std::string filename_sanitized(filename);
858 utility::sanitize_filename(filename_sanitized);
859 return compile(detail::get_loader_ref()(filename_sanitized));
871 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:157
std::string render_string() const
Output a returnable template from this mustache template.
Definition: mustache.h:472
rendered_template render() const
Output a returnable template from this mustache template.
Definition: mustache.h:443
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:455
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:466
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:484
std::string & get_global_template_base_directory_ref()
A base directory not related to any blueprint.
Definition: mustache.h:748
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:811
template_t compile(const std::string &body)
The function that compiles a source into a mustache template.
Definition: mustache.h:734
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:848
void set_global_base(const std::string &path)
Defines the templates directory path at global level. By default is templates/.
Definition: mustache.h:794
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:758
void set_base(const std::string &path)
Defines the templates directory path at route level. By default is templates/.
Definition: mustache.h:781
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:821
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:855
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:869
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