mustache.h 36 KB

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