Crow  1.1
A C++ microframework for the web
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 
18 namespace 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
455  rendered_template render(const context& ctx) const
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
466  rendered_template render(const context&& ctx) const
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  auto& matched = actions_[blockPositions.back()];
546  if (body_.compare(idx, endIdx - idx,
547  body_, matched.start, matched.end - matched.start) != 0)
548  {
549  throw invalid_template_exception(
550  std::string("not matched {{")
551  + matched.tag_char
552  + "{{/ pair: "
553  + body_.substr(matched.start, matched.end - matched.start) + ", "
554  + body_.substr(idx, endIdx - idx)
555  );
556  }
557  matched.pos = static_cast<int>(actions_.size());
558  matched.has_end_match = true;
559  }
560  actions_.emplace_back(tag_char, ActionType::CloseBlock, idx, endIdx, blockPositions.back());
561  blockPositions.pop_back();
562  break;
563  case '^':
564  idx++;
565  while (body_[idx] == ' ')
566  idx++;
567  while (body_[endIdx - 1] == ' ')
568  endIdx--;
569  blockPositions.emplace_back(static_cast<int>(actions_.size()));
570  actions_.emplace_back(tag_char, ActionType::ElseBlock, idx, endIdx);
571  break;
572  case '!':
573  // do nothing action
574  actions_.emplace_back(tag_char, ActionType::Ignore, idx + 1, endIdx);
575  break;
576  case '>': // partial
577  idx++;
578  while (body_[idx] == ' ')
579  idx++;
580  while (body_[endIdx - 1] == ' ')
581  endIdx--;
582  actions_.emplace_back(tag_char, ActionType::Partial, idx, endIdx);
583  break;
584  case '{':
585  if (tag_open != "{{" || tag_close != "}}")
586  throw invalid_template_exception("cannot use triple mustache when delimiter changed");
587 
588  idx++;
589  if (body_[endIdx + 2] != '}')
590  {
591  throw invalid_template_exception("{{{: }}} not matched");
592  }
593  while (body_[idx] == ' ')
594  idx++;
595  while (body_[endIdx - 1] == ' ')
596  endIdx--;
597  actions_.emplace_back(tag_char, ActionType::UnescapeTag, idx, endIdx);
598  current++;
599  break;
600  case '&':
601  idx++;
602  while (body_[idx] == ' ')
603  idx++;
604  while (body_[endIdx - 1] == ' ')
605  endIdx--;
606  actions_.emplace_back(tag_char, ActionType::UnescapeTag, idx, endIdx);
607  break;
608  case '=':
609  // tag itself is no-op
610  idx++;
611  actions_.emplace_back(tag_char, ActionType::Ignore, idx, endIdx);
612  endIdx--;
613  if (body_[endIdx] != '=')
614  throw invalid_template_exception("{{=: not matching = tag: " + body_.substr(idx, endIdx - idx));
615  endIdx--;
616  while (body_[idx] == ' ')
617  idx++;
618  while (body_[endIdx] == ' ')
619  endIdx--;
620  endIdx++;
621  {
622  bool succeeded = false;
623  for (size_t i = idx; i < endIdx; i++)
624  {
625  if (body_[i] == ' ')
626  {
627  tag_open = body_.substr(idx, i - idx);
628  while (body_[i] == ' ')
629  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");
635 
636  if (tag_close.find(" ") != tag_close.npos)
637  throw invalid_template_exception("{{=: invalid open/close tag: " + tag_open + " " + tag_close);
638  succeeded = true;
639  break;
640  }
641  }
642  if (!succeeded)
643  throw invalid_template_exception("{{=: cannot find space between new open/close tags");
644  }
645  break;
646  default:
647  // normal tag case;
648  while (body_[idx] == ' ')
649  idx++;
650  while (body_[endIdx - 1] == ' ')
651  endIdx--;
652  actions_.emplace_back(tag_char, ActionType::Tag, idx, endIdx);
653  break;
654  }
655  }
656 
657  // ensure no unmatched tags
658  for (int i = 0; i < static_cast<int>(actions_.size()); i++)
659  {
660  if (actions_[i].missing_end_pair())
661  {
662  throw invalid_template_exception(
663  std::string("open tag has no matching end tag {{")
664  + actions_[i].tag_char
665  + " {{/ pair: "
666  + body_.substr(actions_[i].start, actions_[i].end - actions_[i].start)
667  );
668  }
669  }
670 
671  // removing standalones
672  for (int i = static_cast<int>(actions_.size()) - 2; i >= 0; i--)
673  {
674  if (actions_[i].t == ActionType::Tag || actions_[i].t == ActionType::UnescapeTag)
675  continue;
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;
680  int j, k;
681  for (j = fragment_before.second - 1; j >= fragment_before.first; j--)
682  {
683  if (body_[j] != ' ')
684  {
685  all_space_before = false;
686  break;
687  }
688  }
689  if (all_space_before && i > 0)
690  continue;
691  if (!all_space_before && body_[j] != '\n')
692  continue;
693  bool all_space_after = true;
694  for (k = fragment_after.first; k < static_cast<int>(body_.size()) && k < fragment_after.second; k++)
695  {
696  if (body_[k] != ' ')
697  {
698  all_space_after = false;
699  break;
700  }
701  }
702  if (all_space_after && !is_last_action)
703  continue;
704  if (!all_space_after &&
705  !(
706  body_[k] == '\n' ||
707  (body_[k] == '\r' &&
708  k + 1 < static_cast<int>(body_.size()) &&
709  body_[k + 1] == '\n')))
710  continue;
711  if (actions_[i].t == ActionType::Partial)
712  {
713  actions_[i].pos = fragment_before.second - j - 1;
714  }
715  fragment_before.second = j + 1;
716  if (!all_space_after)
717  {
718  if (body_[k] == '\n')
719  k++;
720  else
721  k += 2;
722  fragment_after.first = k;
723  }
724  }
725  }
726 
727  std::vector<std::pair<int, int>> fragments_;
728  std::vector<Action> actions_;
729  std::string body_;
730  };
731 
732  /// \brief The function that compiles a source into a mustache
733  /// template.
734  inline template_t compile(const std::string& body)
735  {
736  return template_t(body);
737  }
738 
739  namespace detail
740  {
741  inline std::string& get_template_base_directory_ref()
742  {
743  static std::string template_base_directory = "templates";
744  return template_base_directory;
745  }
746 
747  /// A base directory not related to any blueprint
749  {
750  static std::string template_base_directory = "templates";
751  return template_base_directory;
752  }
753  } // namespace detail
754 
755  /// \brief The default way that \ref load, \ref load_unsafe,
756  /// \ref load_text and \ref load_text_unsafe use to read the
757  /// contents of a file.
758  inline std::string default_loader(const std::string& filename)
759  {
760  std::string path = detail::get_template_base_directory_ref();
761  std::ifstream inf(utility::join_path(path, filename));
762  if (!inf)
763  {
764  CROW_LOG_WARNING << "Template \"" << filename << "\" not found.";
765  return {};
766  }
767  return {std::istreambuf_iterator<char>(inf), std::istreambuf_iterator<char>()};
768  }
769 
770  namespace detail
771  {
772  inline std::function<std::string(std::string)>& get_loader_ref()
773  {
774  static std::function<std::string(std::string)> loader = default_loader;
775  return loader;
776  }
777  } // namespace detail
778 
779  /// \brief Defines the templates directory path at **route
780  /// level**. By default is `templates/`.
781  inline void set_base(const std::string& path)
782  {
783  auto& base = detail::get_template_base_directory_ref();
784  base = path;
785  if (base.back() != '\\' &&
786  base.back() != '/')
787  {
788  base += '/';
789  }
790  }
791 
792  /// \brief Defines the templates directory path at **global
793  /// level**. By default is `templates/`.
794  inline void set_global_base(const std::string& path)
795  {
796  auto& base = detail::get_global_template_base_directory_ref();
797  base = path;
798  if (base.back() != '\\' &&
799  base.back() != '/')
800  {
801  base += '/';
802  }
803  }
804 
805  /// \brief Change the way that \ref load, \ref load_unsafe,
806  /// \ref load_text and \ref load_text_unsafe reads a file.
807  ///
808  /// By default, the previously mentioned functions load files
809  /// using \ref default_loader, that only reads a file and
810  /// returns a std::string.
811  inline void set_loader(std::function<std::string(std::string)> loader)
812  {
813  detail::get_loader_ref() = std::move(loader);
814  }
815 
816  /// \brief Open, read and sanitize a file but returns a
817  /// std::string without a previous rendering process.
818  ///
819  /// Except for the **sanitize process** this function does the
820  /// almost the same thing that \ref load_text_unsafe.
821  inline std::string load_text(const std::string& filename)
822  {
823  std::string filename_sanitized(filename);
824  utility::sanitize_filename(filename_sanitized);
825  return detail::get_loader_ref()(filename_sanitized);
826  }
827 
828  /// \brief Open and read a file but returns a std::string
829  /// without a previous rendering process.
830  ///
831  /// This function is more like a helper to reduce code like
832  /// this...
833  ///
834  /// ```cpp
835  /// std::ifstream file("home.html");
836  /// return std::string({std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()});
837  /// ```
838  ///
839  /// ... Into this...
840  ///
841  /// ```cpp
842  /// return load("home.html");
843  /// ```
844  ///
845  /// \warning Usually \ref load_text is more recommended to use
846  /// instead because it may prevent some [XSS Attacks](https://en.wikipedia.org/wiki/Cross-site_scripting).
847  /// **Never blindly trust your users!**
848  inline std::string load_text_unsafe(const std::string& filename)
849  {
850  return detail::get_loader_ref()(filename);
851  }
852 
853  /// \brief Open, read and renders a file using a mustache
854  /// compiler. It also sanitize the input before compilation.
855  inline template_t load(const std::string& filename)
856  {
857  std::string filename_sanitized(filename);
858  utility::sanitize_filename(filename_sanitized);
859  return compile(detail::get_loader_ref()(filename_sanitized));
860  }
861 
862  /// \brief Open, read and renders a file using a mustache
863  /// compiler. But it **do not** sanitize the input before
864  /// compilation.
865  ///
866  /// \warning Usually \ref load is more recommended to use
867  /// instead because it may prevent some [XSS Attacks](https://en.wikipedia.org/wiki/Cross-site_scripting).
868  /// **Never blindly trust your users!**
869  inline template_t load_unsafe(const std::string& filename)
870  {
871  return compile(detail::get_loader_ref()(filename));
872  }
873  } // namespace mustache
874 } // 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
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