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()
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  int start;
118  int end;
119  int pos;
120  ActionType t;
121  Action(ActionType t, size_t start, size_t end, size_t pos = 0):
122  start(static_cast<int>(start)), end(static_cast<int>(end)), pos(static_cast<int>(pos)), t(t)
123  {
124  }
125  };
126 
127  /**
128  * \class template_t
129  * \brief Compiled mustache template object.
130  *
131  * \warning Use \ref compile instead.
132  */
134  {
135  public:
136  template_t(std::string body):
137  body_(std::move(body))
138  {
139  // {{ {{# {{/ {{^ {{! {{> {{=
140  parse();
141  }
142 
143  private:
144  std::string tag_name(const Action& action) const
145  {
146  return body_.substr(action.start, action.end - action.start);
147  }
148  auto find_context(const std::string& name, const std::vector<const context*>& stack, bool shouldUseOnlyFirstStackValue = false) const -> std::pair<bool, const context&>
149  {
150  if (name == ".")
151  {
152  return {true, *stack.back()};
153  }
154  static json::wvalue empty_str;
155  empty_str = "";
156 
157  int dotPosition = name.find(".");
158  if (dotPosition == static_cast<int>(name.npos))
159  {
160  for (auto it = stack.rbegin(); it != stack.rend(); ++it)
161  {
162  if ((*it)->t() == json::type::Object)
163  {
164  if ((*it)->count(name))
165  return {true, (**it)[name]};
166  }
167  }
168  }
169  else
170  {
171  std::vector<int> dotPositions;
172  dotPositions.push_back(-1);
173  while (dotPosition != static_cast<int>(name.npos))
174  {
175  dotPositions.push_back(dotPosition);
176  dotPosition = name.find(".", dotPosition + 1);
177  }
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));
183 
184  for (auto it = stack.rbegin(); it != stack.rend(); ++it)
185  {
186  const context* view = *it;
187  bool found = true;
188  for (auto jt = names.begin(); jt != names.end(); ++jt)
189  {
190  if (view->t() == json::type::Object &&
191  view->count(*jt))
192  {
193  view = &(*view)[*jt];
194  }
195  else
196  {
197  if (shouldUseOnlyFirstStackValue)
198  {
199  return {false, empty_str};
200  }
201  found = false;
202  break;
203  }
204  }
205  if (found)
206  return {true, *view};
207  }
208  }
209 
210  return {false, empty_str};
211  }
212 
213  void escape(const std::string& in, std::string& out) const
214  {
215  out.reserve(out.size() + in.size());
216  for (auto it = in.begin(); it != in.end(); ++it)
217  {
218  switch (*it)
219  {
220  case '&': out += "&amp;"; break;
221  case '<': out += "&lt;"; break;
222  case '>': out += "&gt;"; break;
223  case '"': out += "&quot;"; break;
224  case '\'': out += "&#39;"; break;
225  case '/': out += "&#x2F;"; break;
226  case '`': out += "&#x60;"; break;
227  case '=': out += "&#x3D;"; break;
228  default: out += *it; break;
229  }
230  }
231  }
232 
233  bool isTagInsideObjectBlock(const int& current, const std::vector<const context*>& stack) const
234  {
235  int openedBlock = 0;
236  for (int i = current; i > 0; --i)
237  {
238  auto& action = actions_[i - 1];
239 
240  if (action.t == ActionType::OpenBlock)
241  {
242  if (openedBlock == 0 && (*stack.rbegin())->t() == json::type::Object)
243  {
244  return true;
245  }
246  --openedBlock;
247  }
248  else if (action.t == ActionType::CloseBlock)
249  {
250  ++openedBlock;
251  }
252  }
253 
254  return false;
255  }
256 
257  void render_internal(int actionBegin, int actionEnd, std::vector<const context*>& stack, std::string& out, int indent) const
258  {
259  int current = actionBegin;
260 
261  if (indent)
262  out.insert(out.size(), indent, ' ');
263 
264  while (current < actionEnd)
265  {
266  auto& fragment = fragments_[current];
267  auto& action = actions_[current];
268  render_fragment(fragment, indent, out);
269  switch (action.t)
270  {
271  case ActionType::Ignore:
272  // do nothing
273  break;
274  case ActionType::Partial:
275  {
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);
280  }
281  break;
282  case ActionType::UnescapeTag:
283  case ActionType::Tag:
284  {
285  bool shouldUseOnlyFirstStackValue = false;
286  if (isTagInsideObjectBlock(current, stack))
287  {
288  shouldUseOnlyFirstStackValue = true;
289  }
290  auto optional_ctx = find_context(tag_name(action), stack, shouldUseOnlyFirstStackValue);
291  auto& ctx = optional_ctx.second;
292  switch (ctx.t())
293  {
294  case json::type::False:
295  case json::type::True:
296  case json::type::Number:
297  out += ctx.dump();
298  break;
299  case json::type::String:
300  if (action.t == ActionType::Tag)
301  escape(ctx.s, out);
302  else
303  out += ctx.s;
304  break;
305  case json::type::Function:
306  {
307  std::string execute_result = ctx.execute();
308  while (execute_result.find("{{") != std::string::npos)
309  {
310  template_t result_plug(execute_result);
311  execute_result = result_plug.render_string(*(stack[0]));
312  }
313 
314  if (action.t == ActionType::Tag)
315  escape(execute_result, out);
316  else
317  out += execute_result;
318  }
319  break;
320  default:
321  throw std::runtime_error("not implemented tag type" + utility::lexical_cast<std::string>(static_cast<int>(ctx.t())));
322  }
323  }
324  break;
325  case ActionType::ElseBlock:
326  {
327  static context nullContext;
328  auto optional_ctx = find_context(tag_name(action), stack);
329  if (!optional_ctx.first)
330  {
331  stack.emplace_back(&nullContext);
332  break;
333  }
334 
335  auto& ctx = optional_ctx.second;
336  switch (ctx.t())
337  {
338  case json::type::List:
339  if (ctx.l && !ctx.l->empty())
340  current = action.pos;
341  else
342  stack.emplace_back(&nullContext);
343  break;
344  case json::type::False:
345  case json::type::Null:
346  stack.emplace_back(&nullContext);
347  break;
348  default:
349  current = action.pos;
350  break;
351  }
352  break;
353  }
354  case ActionType::OpenBlock:
355  {
356  auto optional_ctx = find_context(tag_name(action), stack);
357  if (!optional_ctx.first)
358  {
359  current = action.pos;
360  break;
361  }
362 
363  auto& ctx = optional_ctx.second;
364  switch (ctx.t())
365  {
366  case json::type::List:
367  if (ctx.l)
368  for (auto it = ctx.l->begin(); it != ctx.l->end(); ++it)
369  {
370  stack.push_back(&*it);
371  render_internal(current + 1, action.pos, stack, out, indent);
372  stack.pop_back();
373  }
374  current = action.pos;
375  break;
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);
381  break;
382  case json::type::False:
383  case json::type::Null:
384  current = action.pos;
385  break;
386  default:
387  throw std::runtime_error("{{#: not implemented context type: " + utility::lexical_cast<std::string>(static_cast<int>(ctx.t())));
388  break;
389  }
390  break;
391  }
392  case ActionType::CloseBlock:
393  stack.pop_back();
394  break;
395  default:
396  throw std::runtime_error("not implemented " + utility::lexical_cast<std::string>(static_cast<int>(action.t)));
397  }
398  current++;
399  }
400  auto& fragment = fragments_[actionEnd];
401  render_fragment(fragment, indent, out);
402  }
403  void render_fragment(const std::pair<int, int> fragment, int indent, std::string& out) const
404  {
405  if (indent)
406  {
407  for (int i = fragment.first; i < fragment.second; i++)
408  {
409  out += body_[i];
410  if (body_[i] == '\n' && i + 1 != static_cast<int>(body_.size()))
411  out.insert(out.size(), indent, ' ');
412  }
413  }
414  else
415  out.insert(out.size(), body_, fragment.first, fragment.second - fragment.first);
416  }
417 
418  public:
419  /// Output a returnable template from this mustache template
421  {
422  context empty_ctx;
423  std::vector<const context*> stack;
424  stack.emplace_back(&empty_ctx);
425 
426  std::string ret;
427  render_internal(0, fragments_.size() - 1, stack, ret, 0);
428  return rendered_template(ret);
429  }
430 
431  /// Apply the values from the context provided and output a returnable template from this mustache template
432  rendered_template render(const context& ctx) const
433  {
434  std::vector<const context*> stack;
435  stack.emplace_back(&ctx);
436 
437  std::string ret;
438  render_internal(0, fragments_.size() - 1, stack, ret, 0);
439  return rendered_template(ret);
440  }
441 
442  /// Apply the values from the context provided and output a returnable template from this mustache template
443  rendered_template render(const context&& ctx) const
444  {
445  return render(ctx);
446  }
447 
448  /// Output a returnable template from this mustache template
449  std::string render_string() const
450  {
451  context empty_ctx;
452  std::vector<const context*> stack;
453  stack.emplace_back(&empty_ctx);
454 
455  std::string ret;
456  render_internal(0, fragments_.size() - 1, stack, ret, 0);
457  return ret;
458  }
459 
460  /// Apply the values from the context provided and output a returnable template from this mustache template
461  std::string render_string(const context& ctx) const
462  {
463  std::vector<const context*> stack;
464  stack.emplace_back(&ctx);
465 
466  std::string ret;
467  render_internal(0, fragments_.size() - 1, stack, ret, 0);
468  return ret;
469  }
470 
471  private:
472  void parse()
473  {
474  std::string tag_open = "{{";
475  std::string tag_close = "}}";
476 
477  std::vector<int> blockPositions;
478 
479  size_t current = 0;
480  while (1)
481  {
482  size_t idx = body_.find(tag_open, current);
483  if (idx == body_.npos)
484  {
485  fragments_.emplace_back(static_cast<int>(current), static_cast<int>(body_.size()));
486  actions_.emplace_back(ActionType::Ignore, 0, 0);
487  break;
488  }
489  fragments_.emplace_back(static_cast<int>(current), static_cast<int>(idx));
490 
491  idx += tag_open.size();
492  size_t endIdx = body_.find(tag_close, idx);
493  if (endIdx == idx)
494  {
495  throw invalid_template_exception("empty tag is not allowed");
496  }
497  if (endIdx == body_.npos)
498  {
499  // error, no matching tag
500  throw invalid_template_exception("not matched opening tag");
501  }
502  current = endIdx + tag_close.size();
503  switch (body_[idx])
504  {
505  case '#':
506  idx++;
507  while (body_[idx] == ' ')
508  idx++;
509  while (body_[endIdx - 1] == ' ')
510  endIdx--;
511  blockPositions.emplace_back(static_cast<int>(actions_.size()));
512  actions_.emplace_back(ActionType::OpenBlock, idx, endIdx);
513  break;
514  case '/':
515  idx++;
516  while (body_[idx] == ' ')
517  idx++;
518  while (body_[endIdx - 1] == ' ')
519  endIdx--;
520  {
521  auto& matched = actions_[blockPositions.back()];
522  if (body_.compare(idx, endIdx - idx,
523  body_, matched.start, matched.end - matched.start) != 0)
524  {
525  throw invalid_template_exception("not matched {{# {{/ pair: " +
526  body_.substr(matched.start, matched.end - matched.start) + ", " +
527  body_.substr(idx, endIdx - idx));
528  }
529  matched.pos = actions_.size();
530  }
531  actions_.emplace_back(ActionType::CloseBlock, idx, endIdx, blockPositions.back());
532  blockPositions.pop_back();
533  break;
534  case '^':
535  idx++;
536  while (body_[idx] == ' ')
537  idx++;
538  while (body_[endIdx - 1] == ' ')
539  endIdx--;
540  blockPositions.emplace_back(static_cast<int>(actions_.size()));
541  actions_.emplace_back(ActionType::ElseBlock, idx, endIdx);
542  break;
543  case '!':
544  // do nothing action
545  actions_.emplace_back(ActionType::Ignore, idx + 1, endIdx);
546  break;
547  case '>': // partial
548  idx++;
549  while (body_[idx] == ' ')
550  idx++;
551  while (body_[endIdx - 1] == ' ')
552  endIdx--;
553  actions_.emplace_back(ActionType::Partial, idx, endIdx);
554  break;
555  case '{':
556  if (tag_open != "{{" || tag_close != "}}")
557  throw invalid_template_exception("cannot use triple mustache when delimiter changed");
558 
559  idx++;
560  if (body_[endIdx + 2] != '}')
561  {
562  throw invalid_template_exception("{{{: }}} not matched");
563  }
564  while (body_[idx] == ' ')
565  idx++;
566  while (body_[endIdx - 1] == ' ')
567  endIdx--;
568  actions_.emplace_back(ActionType::UnescapeTag, idx, endIdx);
569  current++;
570  break;
571  case '&':
572  idx++;
573  while (body_[idx] == ' ')
574  idx++;
575  while (body_[endIdx - 1] == ' ')
576  endIdx--;
577  actions_.emplace_back(ActionType::UnescapeTag, idx, endIdx);
578  break;
579  case '=':
580  // tag itself is no-op
581  idx++;
582  actions_.emplace_back(ActionType::Ignore, idx, endIdx);
583  endIdx--;
584  if (body_[endIdx] != '=')
585  throw invalid_template_exception("{{=: not matching = tag: " + body_.substr(idx, endIdx - idx));
586  endIdx--;
587  while (body_[idx] == ' ')
588  idx++;
589  while (body_[endIdx] == ' ')
590  endIdx--;
591  endIdx++;
592  {
593  bool succeeded = false;
594  for (size_t i = idx; i < endIdx; i++)
595  {
596  if (body_[i] == ' ')
597  {
598  tag_open = body_.substr(idx, i - idx);
599  while (body_[i] == ' ')
600  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");
606 
607  if (tag_close.find(" ") != tag_close.npos)
608  throw invalid_template_exception("{{=: invalid open/close tag: " + tag_open + " " + tag_close);
609  succeeded = true;
610  break;
611  }
612  }
613  if (!succeeded)
614  throw invalid_template_exception("{{=: cannot find space between new open/close tags");
615  }
616  break;
617  default:
618  // normal tag case;
619  while (body_[idx] == ' ')
620  idx++;
621  while (body_[endIdx - 1] == ' ')
622  endIdx--;
623  actions_.emplace_back(ActionType::Tag, idx, endIdx);
624  break;
625  }
626  }
627 
628  // removing standalones
629  for (int i = actions_.size() - 2; i >= 0; i--)
630  {
631  if (actions_[i].t == ActionType::Tag || actions_[i].t == ActionType::UnescapeTag)
632  continue;
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;
637  int j, k;
638  for (j = fragment_before.second - 1; j >= fragment_before.first; j--)
639  {
640  if (body_[j] != ' ')
641  {
642  all_space_before = false;
643  break;
644  }
645  }
646  if (all_space_before && i > 0)
647  continue;
648  if (!all_space_before && body_[j] != '\n')
649  continue;
650  bool all_space_after = true;
651  for (k = fragment_after.first; k < static_cast<int>(body_.size()) && k < fragment_after.second; k++)
652  {
653  if (body_[k] != ' ')
654  {
655  all_space_after = false;
656  break;
657  }
658  }
659  if (all_space_after && !is_last_action)
660  continue;
661  if (!all_space_after &&
662  !(
663  body_[k] == '\n' ||
664  (body_[k] == '\r' &&
665  k + 1 < static_cast<int>(body_.size()) &&
666  body_[k + 1] == '\n')))
667  continue;
668  if (actions_[i].t == ActionType::Partial)
669  {
670  actions_[i].pos = fragment_before.second - j - 1;
671  }
672  fragment_before.second = j + 1;
673  if (!all_space_after)
674  {
675  if (body_[k] == '\n')
676  k++;
677  else
678  k += 2;
679  fragment_after.first = k;
680  }
681  }
682  }
683 
684  std::vector<std::pair<int, int>> fragments_;
685  std::vector<Action> actions_;
686  std::string body_;
687  };
688 
689  /// \brief The function that compiles a source into a mustache
690  /// template.
691  inline template_t compile(const std::string& body)
692  {
693  return template_t(body);
694  }
695 
696  namespace detail
697  {
698  inline std::string& get_template_base_directory_ref()
699  {
700  static std::string template_base_directory = "templates";
701  return template_base_directory;
702  }
703 
704  /// A base directory not related to any blueprint
706  {
707  static std::string template_base_directory = "templates";
708  return template_base_directory;
709  }
710  } // namespace detail
711 
712  /// \brief The default way that \ref load, \ref load_unsafe,
713  /// \ref load_text and \ref load_text_unsafe use to read the
714  /// contents of a file.
715  inline std::string default_loader(const std::string& filename)
716  {
717  std::string path = detail::get_template_base_directory_ref();
718  std::ifstream inf(utility::join_path(path, filename));
719  if (!inf)
720  {
721  CROW_LOG_WARNING << "Template \"" << filename << "\" not found.";
722  return {};
723  }
724  return {std::istreambuf_iterator<char>(inf), std::istreambuf_iterator<char>()};
725  }
726 
727  namespace detail
728  {
729  inline std::function<std::string(std::string)>& get_loader_ref()
730  {
731  static std::function<std::string(std::string)> loader = default_loader;
732  return loader;
733  }
734  } // namespace detail
735 
736  /// \brief Defines the templates directory path at **route
737  /// level**. By default is `templates/`.
738  inline void set_base(const std::string& path)
739  {
740  auto& base = detail::get_template_base_directory_ref();
741  base = path;
742  if (base.back() != '\\' &&
743  base.back() != '/')
744  {
745  base += '/';
746  }
747  }
748 
749  /// \brief Defines the templates directory path at **global
750  /// level**. By default is `templates/`.
751  inline void set_global_base(const std::string& path)
752  {
753  auto& base = detail::get_global_template_base_directory_ref();
754  base = path;
755  if (base.back() != '\\' &&
756  base.back() != '/')
757  {
758  base += '/';
759  }
760  }
761 
762  /// \brief Change the way that \ref load, \ref load_unsafe,
763  /// \ref load_text and \ref load_text_unsafe reads a file.
764  ///
765  /// By default, the previously mentioned functions load files
766  /// using \ref default_loader, that only reads a file and
767  /// returns a std::string.
768  inline void set_loader(std::function<std::string(std::string)> loader)
769  {
770  detail::get_loader_ref() = std::move(loader);
771  }
772 
773  /// \brief Open, read and sanitize a file but returns a
774  /// std::string without a previous rendering process.
775  ///
776  /// Except for the **sanitize process** this function does the
777  /// almost the same thing that \ref load_text_unsafe.
778  inline std::string load_text(const std::string& filename)
779  {
780  std::string filename_sanitized(filename);
781  utility::sanitize_filename(filename_sanitized);
782  return detail::get_loader_ref()(filename_sanitized);
783  }
784 
785  /// \brief Open and read a file but returns a std::string
786  /// without a previous rendering process.
787  ///
788  /// This function is more like a helper to reduce code like
789  /// this...
790  ///
791  /// ```cpp
792  /// std::ifstream file("home.html");
793  /// return std::string({std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()});
794  /// ```
795  ///
796  /// ... Into this...
797  ///
798  /// ```cpp
799  /// return load("home.html");
800  /// ```
801  ///
802  /// \warning Usually \ref load_text is more recommended to use
803  /// instead because it may prevent some [XSS Attacks](https://en.wikipedia.org/wiki/Cross-site_scripting).
804  /// **Never blindly trust your users!**
805  inline std::string load_text_unsafe(const std::string& filename)
806  {
807  return detail::get_loader_ref()(filename);
808  }
809 
810  /// \brief Open, read and renders a file using a mustache
811  /// compiler. It also sanitize the input before compilation.
812  inline template_t load(const std::string& filename)
813  {
814  std::string filename_sanitized(filename);
815  utility::sanitize_filename(filename_sanitized);
816  return compile(detail::get_loader_ref()(filename_sanitized));
817  }
818 
819  /// \brief Open, read and renders a file using a mustache
820  /// compiler. But it **do not** sanitize the input before
821  /// compilation.
822  ///
823  /// \warning Usually \ref load is more recommended to use
824  /// instead because it may prevent some [XSS Attacks](https://en.wikipedia.org/wiki/Cross-site_scripting).
825  /// **Never blindly trust your users!**
826  inline template_t load_unsafe(const std::string& filename)
827  {
828  return compile(detail::get_loader_ref()(filename));
829  }
830  } // namespace mustache
831 } // namespace crow
JSON write value.
Definition: json.h:1289
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