Crow  1.1
A C++ microframework for the web
 
Loading...
Searching...
No Matches
mustache.h
Go to the documentation of this file.
1/**
2 * \file crow/mustache.h
3 * \brief This file includes the definition of the crow::mustache
4 * namespace and its members.
5 */
6
7#pragma once
8#include <string>
9#include <vector>
10#include <fstream>
11#include <iterator>
12#include <functional>
13#include "crow/json.h"
14#include "crow/logging.h"
15#include "crow/returnable.h"
16#include "crow/utility.h"
17
18namespace crow // NOTE: Already documented in "crow/app.h"
19{
20 /**
21 * \namespace crow::mustache
22 * \brief In this namespace is defined most of the functions and
23 * classes related to template rendering.
24 *
25 * If you are here you might want to read these functions and
26 * classes:
27 *
28 * - \ref template_t
29 * - \ref load_text
30 * - \ref load_text_unsafe
31 * - \ref load
32 * - \ref load_unsafe
33 *
34 * As name suggest, crow uses [mustache](https://en.wikipedia.org/wiki/Mustache_(template_system))
35 * as main template rendering system.
36 *
37 * You may be interested in taking a look at the [Templating guide
38 * page](https://crowcpp.org/master/guides/templating/).
39 */
40 namespace mustache
41 {
42 using context = json::wvalue;
43
44 template_t load(const std::string& filename);
45
46 /**
47 * \class invalid_template_exception
48 * \brief Represents compilation error of an template. Throwed
49 * specially at mustache compile time.
50 */
51 class invalid_template_exception : public std::exception
52 {
53 public:
54 invalid_template_exception(const std::string& msg_):
55 msg("crow::mustache error: " + msg_)
56 {}
57 virtual const char* what() const throw() override
58 {
59 return msg.c_str();
60 }
61 std::string msg;
62 };
63
64 /**
65 * \struct rendered_template
66 * \brief Returned object after call the
67 * \ref template_t::render() method. Its intended to be
68 * returned during a **rule declaration**.
69 *
70 * \see \ref CROW_ROUTE
71 * \see \ref CROW_BP_ROUTE
72 */
74 {
76 returnable("text/html") {}
77
78 rendered_template(std::string& body):
79 returnable("text/html"), body_(std::move(body)) {}
80
81 std::string body_;
82
83 std::string dump() const override
84 {
85 return body_;
86 }
87 };
88
89 /**
90 * \enum ActionType
91 * \brief Used in \ref Action to represent different parsing
92 * behaviors.
93 *
94 * \see \ref Action
95 */
96 enum class ActionType
97 {
98 Ignore,
99 Tag,
100 UnescapeTag,
101 OpenBlock,
102 CloseBlock,
103 ElseBlock,
104 Partial,
105 };
106
107 /**
108 * \struct Action
109 * \brief Used during mustache template compilation to
110 * represent parsing actions.
111 *
112 * \see \ref compile
113 * \see \ref template_t
114 */
115 struct Action
116 {
117 bool has_end_match;
118 char tag_char;
119 int start;
120 int end;
121 int pos;
122 ActionType t;
123
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_)
126 {
127 }
128
129 bool missing_end_pair() const {
130 switch (t)
131 {
132 case ActionType::Ignore:
133 case ActionType::Tag:
134 case ActionType::UnescapeTag:
135 case ActionType::CloseBlock:
136 case ActionType::Partial:
137 return false;
138
139 // requires a match
140 case ActionType::OpenBlock:
141 case ActionType::ElseBlock:
142 return !has_end_match;
143
144 default:
145 throw std::logic_error("invalid type");
146 }
147 }
148 };
149
150 /**
151 * \class template_t
152 * \brief Compiled mustache template object.
153 *
154 * \warning Use \ref compile instead.
155 */
157 {
158 public:
159 template_t(std::string body):
160 body_(std::move(body))
161 {
162 // {{ {{# {{/ {{^ {{! {{> {{=
163 parse();
164 }
165
166 private:
167 std::string tag_name(const Action& action) const
168 {
169 return body_.substr(action.start, action.end - action.start);
170 }
171 auto find_context(const std::string& name, const std::vector<const context*>& stack, bool shouldUseOnlyFirstStackValue = false) const -> std::pair<bool, const context&>
172 {
173 if (name == ".")
174 {
175 return {true, *stack.back()};
176 }
177 static json::wvalue empty_str;
178 empty_str = "";
179
180 int dotPosition = name.find(".");
181 if (dotPosition == static_cast<int>(name.npos))
182 {
183 for (auto it = stack.rbegin(); it != stack.rend(); ++it)
184 {
185 if ((*it)->t() == json::type::Object)
186 {
187 if ((*it)->count(name))
188 return {true, (**it)[name]};
189 }
190 }
191 }
192 else
193 {
194 std::vector<int> dotPositions;
195 dotPositions.push_back(-1);
196 while (dotPosition != static_cast<int>(name.npos))
197 {
198 dotPositions.push_back(dotPosition);
199 dotPosition = name.find(".", dotPosition + 1);
200 }
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));
206
207 for (auto it = stack.rbegin(); it != stack.rend(); ++it)
208 {
209 const context* view = *it;
210 bool found = true;
211 for (auto jt = names.begin(); jt != names.end(); ++jt)
212 {
213 if (view->t() == json::type::Object &&
214 view->count(*jt))
215 {
216 view = &(*view)[*jt];
217 }
218 else
219 {
220 if (shouldUseOnlyFirstStackValue)
221 {
222 return {false, empty_str};
223 }
224 found = false;
225 break;
226 }
227 }
228 if (found)
229 return {true, *view};
230 }
231 }
232
233 return {false, empty_str};
234 }
235
236 void escape(const std::string& in, std::string& out) const
237 {
238 out.reserve(out.size() + in.size());
239 for (auto it = in.begin(); it != in.end(); ++it)
240 {
241 switch (*it)
242 {
243 case '&': out += "&amp;"; break;
244 case '<': out += "&lt;"; break;
245 case '>': out += "&gt;"; break;
246 case '"': out += "&quot;"; break;
247 case '\'': out += "&#39;"; break;
248 case '/': out += "&#x2F;"; break;
249 case '`': out += "&#x60;"; break;
250 case '=': out += "&#x3D;"; break;
251 default: out += *it; break;
252 }
253 }
254 }
255
256 bool isTagInsideObjectBlock(const int& current, const std::vector<const context*>& stack) const
257 {
258 int openedBlock = 0;
259 for (int i = current; i > 0; --i)
260 {
261 auto& action = actions_[i - 1];
262
263 if (action.t == ActionType::OpenBlock)
264 {
265 if (openedBlock == 0 && (*stack.rbegin())->t() == json::type::Object)
266 {
267 return true;
268 }
269 --openedBlock;
270 }
271 else if (action.t == ActionType::CloseBlock)
272 {
273 ++openedBlock;
274 }
275 }
276
277 return false;
278 }
279
280 void render_internal(int actionBegin, int actionEnd, std::vector<const context*>& stack, std::string& out, int indent) const
281 {
282 int current = actionBegin;
283
284 if (indent)
285 out.insert(out.size(), indent, ' ');
286
287 while (current < actionEnd)
288 {
289 auto& fragment = fragments_[current];
290 auto& action = actions_[current];
291 render_fragment(fragment, indent, out);
292 switch (action.t)
293 {
294 case ActionType::Ignore:
295 // do nothing
296 break;
297 case ActionType::Partial:
298 {
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);
303 }
304 break;
305 case ActionType::UnescapeTag:
306 case ActionType::Tag:
307 {
308 bool shouldUseOnlyFirstStackValue = false;
309 if (isTagInsideObjectBlock(current, stack))
310 {
311 shouldUseOnlyFirstStackValue = true;
312 }
313 auto optional_ctx = find_context(tag_name(action), stack, shouldUseOnlyFirstStackValue);
314 auto& ctx = optional_ctx.second;
315 switch (ctx.t())
316 {
317 case json::type::False:
318 case json::type::True:
319 case json::type::Number:
320 out += ctx.dump();
321 break;
322 case json::type::String:
323 if (action.t == ActionType::Tag)
324 escape(ctx.s, out);
325 else
326 out += ctx.s;
327 break;
328 case json::type::Function:
329 {
330 std::string execute_result = ctx.execute();
331 while (execute_result.find("{{") != std::string::npos)
332 {
333 template_t result_plug(execute_result);
334 execute_result = result_plug.render_string(*(stack[0]));
335 }
336
337 if (action.t == ActionType::Tag)
338 escape(execute_result, out);
339 else
340 out += execute_result;
341 }
342 break;
343 default:
344 throw std::runtime_error("not implemented tag type" + utility::lexical_cast<std::string>(static_cast<int>(ctx.t())));
345 }
346 }
347 break;
348 case ActionType::ElseBlock:
349 {
350 static context nullContext;
351 auto optional_ctx = find_context(tag_name(action), stack);
352 if (!optional_ctx.first)
353 {
354 stack.emplace_back(&nullContext);
355 break;
356 }
357
358 auto& ctx = optional_ctx.second;
359 switch (ctx.t())
360 {
361 case json::type::List:
362 if (ctx.l && !ctx.l->empty())
363 current = action.pos;
364 else
365 stack.emplace_back(&nullContext);
366 break;
367 case json::type::False:
368 case json::type::Null:
369 stack.emplace_back(&nullContext);
370 break;
371 default:
372 current = action.pos;
373 break;
374 }
375 break;
376 }
377 case ActionType::OpenBlock:
378 {
379 auto optional_ctx = find_context(tag_name(action), stack);
380 if (!optional_ctx.first)
381 {
382 current = action.pos;
383 break;
384 }
385
386 auto& ctx = optional_ctx.second;
387 switch (ctx.t())
388 {
389 case json::type::List:
390 if (ctx.l)
391 for (auto it = ctx.l->begin(); it != ctx.l->end(); ++it)
392 {
393 stack.push_back(&*it);
394 render_internal(current + 1, action.pos, stack, out, indent);
395 stack.pop_back();
396 }
397 current = action.pos;
398 break;
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);
404 break;
405 case json::type::False:
406 case json::type::Null:
407 current = action.pos;
408 break;
409 default:
410 throw std::runtime_error("{{#: not implemented context type: " + utility::lexical_cast<std::string>(static_cast<int>(ctx.t())));
411 break;
412 }
413 break;
414 }
415 case ActionType::CloseBlock:
416 stack.pop_back();
417 break;
418 default:
419 throw std::runtime_error("not implemented " + utility::lexical_cast<std::string>(static_cast<int>(action.t)));
420 }
421 current++;
422 }
423 auto& fragment = fragments_[actionEnd];
424 render_fragment(fragment, indent, out);
425 }
426 void render_fragment(const std::pair<int, int> fragment, int indent, std::string& out) const
427 {
428 if (indent)
429 {
430 for (int i = fragment.first; i < fragment.second; i++)
431 {
432 out += body_[i];
433 if (body_[i] == '\n' && i + 1 != static_cast<int>(body_.size()))
434 out.insert(out.size(), indent, ' ');
435 }
436 }
437 else
438 out.insert(out.size(), body_, fragment.first, fragment.second - fragment.first);
439 }
440
441 public:
442 /// Output a returnable template from this mustache template
444 {
445 context empty_ctx;
446 std::vector<const context*> stack;
447 stack.emplace_back(&empty_ctx);
448
449 std::string ret;
450 render_internal(0, fragments_.size() - 1, stack, ret, 0);
451 return rendered_template(ret);
452 }
453
454 /// Apply the values from the context provided and output a returnable template from this mustache template
456 {
457 std::vector<const context*> stack;
458 stack.emplace_back(&ctx);
459
460 std::string ret;
461 render_internal(0, fragments_.size() - 1, stack, ret, 0);
462 return rendered_template(ret);
463 }
464
465 /// Apply the values from the context provided and output a returnable template from this mustache template
467 {
468 return render(ctx);
469 }
470
471 /// Output a returnable template from this mustache template
472 std::string render_string() const
473 {
474 context empty_ctx;
475 std::vector<const context*> stack;
476 stack.emplace_back(&empty_ctx);
477
478 std::string ret;
479 render_internal(0, fragments_.size() - 1, stack, ret, 0);
480 return ret;
481 }
482
483 /// Apply the values from the context provided and output a returnable template from this mustache template
484 std::string render_string(const context& ctx) const
485 {
486 std::vector<const context*> stack;
487 stack.emplace_back(&ctx);
488
489 std::string ret;
490 render_internal(0, fragments_.size() - 1, stack, ret, 0);
491 return ret;
492 }
493
494 private:
495 void parse()
496 {
497 std::string tag_open = "{{";
498 std::string tag_close = "}}";
499
500 std::vector<int> blockPositions;
501
502 size_t current = 0;
503 while (1)
504 {
505 size_t idx = body_.find(tag_open, current);
506 if (idx == body_.npos)
507 {
508 fragments_.emplace_back(static_cast<int>(current), static_cast<int>(body_.size()));
509 actions_.emplace_back('!', ActionType::Ignore, 0, 0);
510 break;
511 }
512 fragments_.emplace_back(static_cast<int>(current), static_cast<int>(idx));
513
514 idx += tag_open.size();
515 size_t endIdx = body_.find(tag_close, idx);
516 if (endIdx == idx)
517 {
518 throw invalid_template_exception("empty tag is not allowed");
519 }
520 if (endIdx == body_.npos)
521 {
522 // error, no matching tag
523 throw invalid_template_exception("not matched opening tag");
524 }
525 current = endIdx + tag_close.size();
526 char tag_char = body_[idx];
527 switch (tag_char)
528 {
529 case '#':
530 idx++;
531 while (body_[idx] == ' ')
532 idx++;
533 while (body_[endIdx - 1] == ' ')
534 endIdx--;
535 blockPositions.emplace_back(static_cast<int>(actions_.size()));
536 actions_.emplace_back(tag_char, ActionType::OpenBlock, idx, endIdx);
537 break;
538 case '/':
539 idx++;
540 while (body_[idx] == ' ')
541 idx++;
542 while (body_[endIdx - 1] == ' ')
543 endIdx--;
544 {
545 if (blockPositions.empty())
546 {
547 throw invalid_template_exception(
548 std::string("unexpected closing tag: ")
549 + body_.substr(idx, endIdx - idx)
550 );
551 }
552 auto& matched = actions_[blockPositions.back()];
553 if (body_.compare(idx, endIdx - idx,
554 body_, matched.start, matched.end - matched.start) != 0)
555 {
556 throw invalid_template_exception(
557 std::string("not matched {{")
558 + matched.tag_char
559 + "{{/ pair: "
560 + body_.substr(matched.start, matched.end - matched.start) + ", "
561 + body_.substr(idx, endIdx - idx)
562 );
563 }
564 matched.pos = static_cast<int>(actions_.size());
565 matched.has_end_match = true;
566 }
567 actions_.emplace_back(tag_char, ActionType::CloseBlock, idx, endIdx, blockPositions.back());
568 blockPositions.pop_back();
569 break;
570 case '^':
571 idx++;
572 while (body_[idx] == ' ')
573 idx++;
574 while (body_[endIdx - 1] == ' ')
575 endIdx--;
576 blockPositions.emplace_back(static_cast<int>(actions_.size()));
577 actions_.emplace_back(tag_char, ActionType::ElseBlock, idx, endIdx);
578 break;
579 case '!':
580 // do nothing action
581 actions_.emplace_back(tag_char, ActionType::Ignore, idx + 1, endIdx);
582 break;
583 case '>': // partial
584 idx++;
585 while (body_[idx] == ' ')
586 idx++;
587 while (body_[endIdx - 1] == ' ')
588 endIdx--;
589 actions_.emplace_back(tag_char, ActionType::Partial, idx, endIdx);
590 break;
591 case '{':
592 if (tag_open != "{{" || tag_close != "}}")
593 throw invalid_template_exception("cannot use triple mustache when delimiter changed");
594
595 idx++;
596 if (body_[endIdx + 2] != '}')
597 {
598 throw invalid_template_exception("{{{: }}} not matched");
599 }
600 while (body_[idx] == ' ')
601 idx++;
602 while (body_[endIdx - 1] == ' ')
603 endIdx--;
604 actions_.emplace_back(tag_char, ActionType::UnescapeTag, idx, endIdx);
605 current++;
606 break;
607 case '&':
608 idx++;
609 while (body_[idx] == ' ')
610 idx++;
611 while (body_[endIdx - 1] == ' ')
612 endIdx--;
613 actions_.emplace_back(tag_char, ActionType::UnescapeTag, idx, endIdx);
614 break;
615 case '=':
616 // tag itself is no-op
617 idx++;
618 actions_.emplace_back(tag_char, ActionType::Ignore, idx, endIdx);
619 endIdx--;
620 if (body_[endIdx] != '=')
621 throw invalid_template_exception("{{=: not matching = tag: " + body_.substr(idx, endIdx - idx));
622 endIdx--;
623 while (body_[idx] == ' ')
624 idx++;
625 while (body_[endIdx] == ' ')
626 endIdx--;
627 endIdx++;
628 {
629 bool succeeded = false;
630 for (size_t i = idx; i < endIdx; i++)
631 {
632 if (body_[i] == ' ')
633 {
634 tag_open = body_.substr(idx, i - idx);
635 while (body_[i] == ' ')
636 i++;
637 tag_close = body_.substr(i, endIdx - i);
638 if (tag_open.empty())
639 throw invalid_template_exception("{{=: empty open tag");
640 if (tag_close.empty())
641 throw invalid_template_exception("{{=: empty close tag");
642
643 if (tag_close.find(" ") != tag_close.npos)
644 throw invalid_template_exception("{{=: invalid open/close tag: " + tag_open + " " + tag_close);
645 succeeded = true;
646 break;
647 }
648 }
649 if (!succeeded)
650 throw invalid_template_exception("{{=: cannot find space between new open/close tags");
651 }
652 break;
653 default:
654 // normal tag case;
655 while (body_[idx] == ' ')
656 idx++;
657 while (body_[endIdx - 1] == ' ')
658 endIdx--;
659 actions_.emplace_back(tag_char, ActionType::Tag, idx, endIdx);
660 break;
661 }
662 }
663
664 // ensure no unmatched tags
665 for (int i = 0; i < static_cast<int>(actions_.size()); i++)
666 {
667 if (actions_[i].missing_end_pair())
668 {
669 throw invalid_template_exception(
670 std::string("open tag has no matching end tag {{")
671 + actions_[i].tag_char
672 + " {{/ pair: "
673 + body_.substr(actions_[i].start, actions_[i].end - actions_[i].start)
674 );
675 }
676 }
677
678 // removing standalones
679 for (int i = static_cast<int>(actions_.size()) - 2; i >= 0; i--)
680 {
681 if (actions_[i].t == ActionType::Tag || actions_[i].t == ActionType::UnescapeTag)
682 continue;
683 auto& fragment_before = fragments_[i];
684 auto& fragment_after = fragments_[i + 1];
685 bool is_last_action = i == static_cast<int>(actions_.size()) - 2;
686 bool all_space_before = true;
687 int j, k;
688 for (j = fragment_before.second - 1; j >= fragment_before.first; j--)
689 {
690 if (body_[j] != ' ')
691 {
692 all_space_before = false;
693 break;
694 }
695 }
696 if (all_space_before && i > 0)
697 continue;
698 if (!all_space_before && body_[j] != '\n')
699 continue;
700 bool all_space_after = true;
701 for (k = fragment_after.first; k < static_cast<int>(body_.size()) && k < fragment_after.second; k++)
702 {
703 if (body_[k] != ' ')
704 {
705 all_space_after = false;
706 break;
707 }
708 }
709 if (all_space_after && !is_last_action)
710 continue;
711 if (!all_space_after &&
712 !(
713 body_[k] == '\n' ||
714 (body_[k] == '\r' &&
715 k + 1 < static_cast<int>(body_.size()) &&
716 body_[k + 1] == '\n')))
717 continue;
718 if (actions_[i].t == ActionType::Partial)
719 {
720 actions_[i].pos = fragment_before.second - j - 1;
721 }
722 fragment_before.second = j + 1;
723 if (!all_space_after)
724 {
725 if (body_[k] == '\n')
726 k++;
727 else
728 k += 2;
729 fragment_after.first = k;
730 }
731 }
732 }
733
734 std::vector<std::pair<int, int>> fragments_;
735 std::vector<Action> actions_;
736 std::string body_;
737 };
738
739 /// \brief The function that compiles a source into a mustache
740 /// template.
741 inline template_t compile(const std::string& body)
742 {
743 return template_t(body);
744 }
745
746 namespace detail
747 {
748 inline std::string& get_template_base_directory_ref()
749 {
750 static std::string template_base_directory = "templates";
751 return template_base_directory;
752 }
753
754 /// A base directory not related to any blueprint
755 inline std::string& get_global_template_base_directory_ref()
756 {
757 static std::string template_base_directory = "templates";
758 return template_base_directory;
759 }
760 } // namespace detail
761
762 /// \brief The default way that \ref load, \ref load_unsafe,
763 /// \ref load_text and \ref load_text_unsafe use to read the
764 /// contents of a file.
765 inline std::string default_loader(const std::string& filename)
766 {
767 std::string path = detail::get_template_base_directory_ref();
768 std::ifstream inf(utility::join_path(path, filename));
769 if (!inf)
770 {
771 CROW_LOG_WARNING << "Template \"" << filename << "\" not found.";
772 return {};
773 }
774 return {std::istreambuf_iterator<char>(inf), std::istreambuf_iterator<char>()};
775 }
776
777 namespace detail
778 {
779 inline std::function<std::string(std::string)>& get_loader_ref()
780 {
781 static std::function<std::string(std::string)> loader = default_loader;
782 return loader;
783 }
784 } // namespace detail
785
786 /// \brief Defines the templates directory path at **route
787 /// level**. By default is `templates/`.
788 inline void set_base(const std::string& path)
789 {
790 auto& base = detail::get_template_base_directory_ref();
791 base = path;
792 if (base.back() != '\\' &&
793 base.back() != '/')
794 {
795 base += '/';
796 }
797 }
798
799 /// \brief Defines the templates directory path at **global
800 /// level**. By default is `templates/`.
801 inline void set_global_base(const std::string& path)
802 {
803 auto& base = detail::get_global_template_base_directory_ref();
804 base = path;
805 if (base.back() != '\\' &&
806 base.back() != '/')
807 {
808 base += '/';
809 }
810 }
811
812 /// \brief Change the way that \ref load, \ref load_unsafe,
813 /// \ref load_text and \ref load_text_unsafe reads a file.
814 ///
815 /// By default, the previously mentioned functions load files
816 /// using \ref default_loader, that only reads a file and
817 /// returns a std::string.
818 inline void set_loader(std::function<std::string(std::string)> loader)
819 {
820 detail::get_loader_ref() = std::move(loader);
821 }
822
823 /// \brief Open, read and sanitize a file but returns a
824 /// std::string without a previous rendering process.
825 ///
826 /// Except for the **sanitize process** this function does the
827 /// almost the same thing that \ref load_text_unsafe.
828 inline std::string load_text(const std::string& filename)
829 {
830 std::string filename_sanitized(filename);
831 utility::sanitize_filename(filename_sanitized);
832 return detail::get_loader_ref()(filename_sanitized);
833 }
834
835 /// \brief Open and read a file but returns a std::string
836 /// without a previous rendering process.
837 ///
838 /// This function is more like a helper to reduce code like
839 /// this...
840 ///
841 /// ```cpp
842 /// std::ifstream file("home.html");
843 /// return std::string({std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()});
844 /// ```
845 ///
846 /// ... Into this...
847 ///
848 /// ```cpp
849 /// return load("home.html");
850 /// ```
851 ///
852 /// \warning Usually \ref load_text is more recommended to use
853 /// instead because it may prevent some [XSS Attacks](https://en.wikipedia.org/wiki/Cross-site_scripting).
854 /// **Never blindly trust your users!**
855 inline std::string load_text_unsafe(const std::string& filename)
856 {
857 return detail::get_loader_ref()(filename);
858 }
859
860 /// \brief Open, read and renders a file using a mustache
861 /// compiler. It also sanitize the input before compilation.
862 inline template_t load(const std::string& filename)
863 {
864 std::string filename_sanitized(filename);
865 utility::sanitize_filename(filename_sanitized);
866 return compile(detail::get_loader_ref()(filename_sanitized));
867 }
868
869 /// \brief Open, read and renders a file using a mustache
870 /// compiler. But it **do not** sanitize the input before
871 /// compilation.
872 ///
873 /// \warning Usually \ref load is more recommended to use
874 /// instead because it may prevent some [XSS Attacks](https://en.wikipedia.org/wiki/Cross-site_scripting).
875 /// **Never blindly trust your users!**
876 inline template_t load_unsafe(const std::string& filename)
877 {
878 return compile(detail::get_loader_ref()(filename));
879 }
880 } // namespace mustache
881} // namespace crow
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
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:818
template_t compile(const std::string &body)
The function that compiles a source into a mustache template.
Definition mustache.h:741
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:855
void set_global_base(const std::string &path)
Defines the templates directory path at global level. By default is templates/.
Definition mustache.h:801
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:765
void set_base(const std::string &path)
Defines the templates directory path at route level. By default is templates/.
Definition mustache.h:788
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:828
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:862
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:876
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