remove mustache
This commit is contained in:
		@@ -6,13 +6,6 @@ NNTPCHAN_SRC := $(wildcard $(NNTPCHAN_PATH)/*.cpp)
 | 
				
			|||||||
NNTPCHAN_HDR := $(wildcard $(NNTPCHAN_PATH)/*.hpp)
 | 
					NNTPCHAN_HDR := $(wildcard $(NNTPCHAN_PATH)/*.hpp)
 | 
				
			||||||
NNTPCHAN_OBJ := $(NNTPCHAN_SRC:.cpp=.o)
 | 
					NNTPCHAN_OBJ := $(NNTPCHAN_SRC:.cpp=.o)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MUSTACHE_PATH = $(REPO)/libmustache
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
MUSTACHE_SRC := $(wildcard $(MUSTACHE_PATH)/*.cpp)
 | 
					 | 
				
			||||||
MUSTACHE_SRC += $(wildcard $(MUSTACHE_PATH)/*/*.cpp)
 | 
					 | 
				
			||||||
MUSTACHE_HDR := $(wildcard $(MUSTACHE_PATH)/*.hpp)
 | 
					 | 
				
			||||||
MUSTACHE_OBJ := $(MUSTACHE_SRC:.cpp=.o)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
HEADERS_PATH=$(REPO)/include
 | 
					HEADERS_PATH=$(REPO)/include
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TOOL_PATH := $(REPO)/tools
 | 
					TOOL_PATH := $(REPO)/tools
 | 
				
			||||||
@@ -20,19 +13,24 @@ TOOL_PATH := $(REPO)/tools
 | 
				
			|||||||
TOOL_SRC := $(wildcard $(TOOL_PATH)/*.cpp)
 | 
					TOOL_SRC := $(wildcard $(TOOL_PATH)/*.cpp)
 | 
				
			||||||
TOOLS := $(TOOL_SRC:.cpp=)
 | 
					TOOLS := $(TOOL_SRC:.cpp=)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SRCS = $(NNTPCHAN_SRC) $(MUSTACHE_SRC) $(TOOL_SRC)
 | 
					SRCS = $(NNTPCHAN_SRC) $(TOOL_SRC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
OBJ := $(NNTPCHAN_OBJ)
 | 
					OBJ := $(NNTPCHAN_OBJ)
 | 
				
			||||||
OBJ += $(MUSTACHE_OBJ)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST = $(REPO)/test
 | 
					TEST = $(REPO)/test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DAEMON_SRC = $(REPO)/daemon
 | 
					DAEMON_SRC = $(REPO)/daemon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PKGS := libsodium
 | 
					ifeq ($(shell uname -s) "FreeBSD")
 | 
				
			||||||
 | 
						SODIUM_INC = /usr/local/include
 | 
				
			||||||
 | 
						SODIUM_LIB = /usr/local/lib/libsodium.a
 | 
				
			||||||
 | 
					else 
 | 
				
			||||||
 | 
						SODIUM_INC = $(shell pkg-config --cflags libsodium)
 | 
				
			||||||
 | 
						SODIUM_LIB = $(shell pkg-config --libs --static libsodium)	
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LD_FLAGS := $(shell pkg-config --libs $(PKGS)) -lstdc++fs
 | 
					LD_FLAGS :=  -lstdc++fs $(SODIUM_LIB)
 | 
				
			||||||
INC_FLAGS := $(shell pkg-config --cflags $(PKGS)) -I$(HEADERS_PATH)
 | 
					INC_FLAGS := -I$(HEADERS_PATH) $(SODIUM_INC)
 | 
				
			||||||
REQUIRED_CXXFLAGS = -std=c++17 -Wall -Wextra -Werror -pedantic $(INC_FLAGS)
 | 
					REQUIRED_CXXFLAGS = -std=c++17 -Wall -Wextra -Werror -pedantic $(INC_FLAGS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEBUG = 1
 | 
					DEBUG = 1
 | 
				
			||||||
@@ -44,9 +42,8 @@ endif
 | 
				
			|||||||
CXXFLAGS += $(REQUIRED_CXXFLAGS)
 | 
					CXXFLAGS += $(REQUIRED_CXXFLAGS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
NNTPCHAN_LIB = $(REPO)/libnntpchan.a
 | 
					NNTPCHAN_LIB = $(REPO)/libnntpchan.a
 | 
				
			||||||
MUSTACHE_LIB = $(REPO)/libmustache.a
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
LIBS = $(NNTPCHAN_LIB) $(MUSTACHE_LIB)
 | 
					LIBS = $(NNTPCHAN_LIB)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXE = $(REPO)/nntpd
 | 
					EXE = $(REPO)/nntpd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -58,9 +55,6 @@ format:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
build: $(EXE) tools
 | 
					build: $(EXE) tools
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(MUSTACHE_LIB): $(MUSTACHE_OBJ)
 | 
					 | 
				
			||||||
	$(AR) -r $(MUSTACHE_LIB) $(MUSTACHE_OBJ)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$(NNTPCHAN_LIB): $(NNTPCHAN_OBJ)
 | 
					$(NNTPCHAN_LIB): $(NNTPCHAN_OBJ)
 | 
				
			||||||
	$(AR) -r $(NNTPCHAN_LIB) $(NNTPCHAN_OBJ)
 | 
						$(AR) -r $(NNTPCHAN_LIB) $(NNTPCHAN_OBJ)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,14 +6,14 @@ requirements:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
* C++17 compiler
 | 
					* C++17 compiler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* pkg-config
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* libsodium 1.x
 | 
					* libsodium 1.x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* boost variant (for now)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* GNU Make
 | 
					* GNU Make
 | 
				
			||||||
 | 
					
 | 
				
			||||||
building:
 | 
					building on freebsd:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $ make
 | 
					    $ gmake
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					building on Linux:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $ make
 | 
				
			||||||
@@ -1,113 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
#include <map>
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
#include <memory>
 | 
					 | 
				
			||||||
#include <functional>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <boost/variant.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct config {
 | 
					 | 
				
			||||||
  static std::function<std::string(const std::string&)> escape;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace internal {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template<class N>
 | 
					 | 
				
			||||||
class object_t {
 | 
					 | 
				
			||||||
 public:
 | 
					 | 
				
			||||||
  const N& at(const std::string& name) const {
 | 
					 | 
				
			||||||
    cache[name] = (methods.at(name))();
 | 
					 | 
				
			||||||
    return cache[name];
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool has(const std::string name) const {
 | 
					 | 
				
			||||||
    return methods.count(name) != 0;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 protected:
 | 
					 | 
				
			||||||
  template<class S>
 | 
					 | 
				
			||||||
  void register_methods(S* s, std::map<std::string,N(S::*)()> methods) {
 | 
					 | 
				
			||||||
    for(auto& item: methods)
 | 
					 | 
				
			||||||
      this->methods.insert({item.first, std::bind(item.second, s)});
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 private:
 | 
					 | 
				
			||||||
  std::map<std::string, std::function<N()>> methods;
 | 
					 | 
				
			||||||
  mutable std::map<std::string, N> cache;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template<class T, class N>
 | 
					 | 
				
			||||||
class is_fun {
 | 
					 | 
				
			||||||
 private:
 | 
					 | 
				
			||||||
  using not_fun = char;
 | 
					 | 
				
			||||||
  using fun_without_args = char[2];
 | 
					 | 
				
			||||||
  using fun_with_args = char[3];
 | 
					 | 
				
			||||||
  template <typename U, U> struct really_has;
 | 
					 | 
				
			||||||
  template <typename C> static fun_without_args& test(
 | 
					 | 
				
			||||||
      really_has<N(C::*)() const, &C::operator()>*);
 | 
					 | 
				
			||||||
  template <typename C> static fun_with_args& test(
 | 
					 | 
				
			||||||
      really_has<N(C::*)(const std::string&) const,
 | 
					 | 
				
			||||||
      &C::operator()>*);
 | 
					 | 
				
			||||||
  template <typename> static not_fun& test(...);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 public:
 | 
					 | 
				
			||||||
  static bool const no_args = sizeof(test<T>(0)) == sizeof(fun_without_args);
 | 
					 | 
				
			||||||
  static bool const has_args = sizeof(test<T>(0)) == sizeof(fun_with_args);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template<class N>
 | 
					 | 
				
			||||||
using node_renderer = std::function<std::string(const N& n)>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template<class N>
 | 
					 | 
				
			||||||
class lambda_t {
 | 
					 | 
				
			||||||
 public:
 | 
					 | 
				
			||||||
  template<class F>
 | 
					 | 
				
			||||||
  lambda_t(F f, typename std::enable_if<is_fun<F, N>::no_args>::type* = 0):
 | 
					 | 
				
			||||||
      fun([f](node_renderer<N> renderer, const std::string&) {
 | 
					 | 
				
			||||||
        return renderer(f());
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  template<class F>
 | 
					 | 
				
			||||||
  lambda_t(F f, typename std::enable_if<is_fun<F, N>::has_args>::type* = 0):
 | 
					 | 
				
			||||||
      fun([f](node_renderer<N> renderer, const std::string& text) {
 | 
					 | 
				
			||||||
        return renderer(f(text));
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::string operator()(node_renderer<N> renderer,
 | 
					 | 
				
			||||||
      const std::string& text = "") const
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    return fun(renderer, text);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 private:
 | 
					 | 
				
			||||||
  std::function<std::string(node_renderer<N> renderer, const std::string&)> fun;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using node = boost::make_recursive_variant<
 | 
					 | 
				
			||||||
    std::nullptr_t, std::string, int, double, bool,
 | 
					 | 
				
			||||||
    internal::lambda_t<boost::recursive_variant_>,
 | 
					 | 
				
			||||||
    std::shared_ptr<internal::object_t<boost::recursive_variant_>>,
 | 
					 | 
				
			||||||
    std::map<const std::string, boost::recursive_variant_>,
 | 
					 | 
				
			||||||
    std::vector<boost::recursive_variant_>>::type;
 | 
					 | 
				
			||||||
using object = internal::object_t<node>;
 | 
					 | 
				
			||||||
using lambda = internal::lambda_t<node>;
 | 
					 | 
				
			||||||
using map = std::map<const std::string, node>;
 | 
					 | 
				
			||||||
using array = std::vector<node>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string render(
 | 
					 | 
				
			||||||
    const std::string& tmplt,
 | 
					 | 
				
			||||||
    const node& root,
 | 
					 | 
				
			||||||
    const std::map<std::string,std::string>& partials =
 | 
					 | 
				
			||||||
        std::map<std::string,std::string>());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -26,7 +26,13 @@ typedef std::tuple<PostHeader, PostBody, Attachments> Post;
 | 
				
			|||||||
// a thread (many posts in post order)
 | 
					// a thread (many posts in post order)
 | 
				
			||||||
typedef std::vector<Post> Thread;
 | 
					typedef std::vector<Post> Thread;
 | 
				
			||||||
// a board page is many threads in bump order
 | 
					// a board page is many threads in bump order
 | 
				
			||||||
typedef std::vector<Thread> BoardPage;
 | 
					struct BoardPage
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  std::vector<Thread> threads = {};
 | 
				
			||||||
 | 
					  std::string name = "";
 | 
				
			||||||
 | 
					  uint32_t pageno = 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline const std::string &GetFilename(const PostAttachment &att) { return std::get<0>(att); }
 | 
					static inline const std::string &GetFilename(const PostAttachment &att) { return std::get<0>(att); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,13 +12,16 @@ namespace nntpchan
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
struct TemplateEngine
 | 
					struct TemplateEngine
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  typedef std::map<std::string, std::variant<nntpchan::model::Model, std::string>> Args_t;
 | 
					  virtual ~TemplateEngine() {};
 | 
				
			||||||
  virtual bool WriteTemplate(const fs::path &template_fpath, const Args_t &args, const FileHandle_ptr &out) = 0;
 | 
					  virtual bool WriteBoardPage(const nntpchan::model::BoardPage & page, const FileHandle_ptr &out) = 0;
 | 
				
			||||||
 | 
					  virtual bool WriteThreadPage(const nntpchan::model::Thread & thread, const FileHandle_ptr &out) = 0;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TemplateEngine *CreateTemplateEngine(const std::string &dialect);
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef std::unique_ptr<TemplateEngine> TemplateEngine_ptr;
 | 
					typedef std::unique_ptr<TemplateEngine> TemplateEngine_ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TemplateEngine * CreateTemplateEngine(const std::string &dialect);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
#include <iostream>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "mstch/mstch.hpp"
 | 
					 | 
				
			||||||
#include "render_context.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using namespace mstch;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::function<std::string(const std::string &)> mstch::config::escape;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string mstch::render(const std::string &tmplt, const node &root,
 | 
					 | 
				
			||||||
                          const std::map<std::string, std::string> &partials)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  std::map<std::string, template_type> partial_templates;
 | 
					 | 
				
			||||||
  for (auto &partial : partials)
 | 
					 | 
				
			||||||
    partial_templates.insert({partial.first, {partial.second}});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return render_context(root, partial_templates).render(tmplt);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,62 +0,0 @@
 | 
				
			|||||||
#include "render_context.hpp"
 | 
					 | 
				
			||||||
#include "state/outside_section.hpp"
 | 
					 | 
				
			||||||
#include "visitor/get_token.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using namespace mstch;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const mstch::node render_context::null_node;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
render_context::push::push(render_context &context, const mstch::node &node) : m_context(context)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  context.m_nodes.emplace_front(node);
 | 
					 | 
				
			||||||
  context.m_node_ptrs.emplace_front(&node);
 | 
					 | 
				
			||||||
  context.m_state.push(std::unique_ptr<render_state>(new outside_section));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
render_context::push::~push()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  m_context.m_nodes.pop_front();
 | 
					 | 
				
			||||||
  m_context.m_node_ptrs.pop_front();
 | 
					 | 
				
			||||||
  m_context.m_state.pop();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string render_context::push::render(const template_type &templt) { return m_context.render(templt); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
render_context::render_context(const mstch::node &node, const std::map<std::string, template_type> &partials)
 | 
					 | 
				
			||||||
    : m_partials(partials), m_nodes(1, node), m_node_ptrs(1, &node)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  m_state.push(std::unique_ptr<render_state>(new outside_section));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const mstch::node &render_context::find_node(const std::string &token, std::list<node const *> current_nodes)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  if (token != "." && token.find('.') != std::string::npos)
 | 
					 | 
				
			||||||
    return find_node(token.substr(token.rfind('.') + 1),
 | 
					 | 
				
			||||||
                     {&find_node(token.substr(0, token.rfind('.')), current_nodes)});
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
    for (auto &node : current_nodes)
 | 
					 | 
				
			||||||
      if (visit(has_token(token), *node))
 | 
					 | 
				
			||||||
        return visit(get_token(token, *node), *node);
 | 
					 | 
				
			||||||
  return null_node;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const mstch::node &render_context::get_node(const std::string &token) { return find_node(token, m_node_ptrs); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string render_context::render(const template_type &templt, const std::string &prefix)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  std::string output;
 | 
					 | 
				
			||||||
  bool prev_eol = true;
 | 
					 | 
				
			||||||
  for (auto &token : templt)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    if (prev_eol && prefix.length() != 0)
 | 
					 | 
				
			||||||
      output += m_state.top()->render(*this, {prefix});
 | 
					 | 
				
			||||||
    output += m_state.top()->render(*this, token);
 | 
					 | 
				
			||||||
    prev_eol = token.eol();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return output;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string render_context::render_partial(const std::string &partial_name, const std::string &prefix)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  return m_partials.count(partial_name) ? render(m_partials.at(partial_name), prefix) : "";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,47 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <deque>
 | 
					 | 
				
			||||||
#include <list>
 | 
					 | 
				
			||||||
#include <sstream>
 | 
					 | 
				
			||||||
#include <stack>
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "state/render_state.hpp"
 | 
					 | 
				
			||||||
#include "template_type.hpp"
 | 
					 | 
				
			||||||
#include <mstch/mstch.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class render_context
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  class push
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
  public:
 | 
					 | 
				
			||||||
    push(render_context &context, const mstch::node &node = {});
 | 
					 | 
				
			||||||
    ~push();
 | 
					 | 
				
			||||||
    std::string render(const template_type &templt);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private:
 | 
					 | 
				
			||||||
    render_context &m_context;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  render_context(const mstch::node &node, const std::map<std::string, template_type> &partials);
 | 
					 | 
				
			||||||
  const mstch::node &get_node(const std::string &token);
 | 
					 | 
				
			||||||
  std::string render(const template_type &templt, const std::string &prefix = "");
 | 
					 | 
				
			||||||
  std::string render_partial(const std::string &partial_name, const std::string &prefix);
 | 
					 | 
				
			||||||
  template <class T, class... Args> void set_state(Args &&... args)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    m_state.top() = std::unique_ptr<render_state>(new T(std::forward<Args>(args)...));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
  static const mstch::node null_node;
 | 
					 | 
				
			||||||
  const mstch::node &find_node(const std::string &token, std::list<node const *> current_nodes);
 | 
					 | 
				
			||||||
  std::map<std::string, template_type> m_partials;
 | 
					 | 
				
			||||||
  std::deque<mstch::node> m_nodes;
 | 
					 | 
				
			||||||
  std::list<const mstch::node *> m_node_ptrs;
 | 
					 | 
				
			||||||
  std::stack<std::unique_ptr<render_state>> m_state;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,36 +0,0 @@
 | 
				
			|||||||
#include "in_section.hpp"
 | 
					 | 
				
			||||||
#include "../visitor/is_node_empty.hpp"
 | 
					 | 
				
			||||||
#include "../visitor/render_section.hpp"
 | 
					 | 
				
			||||||
#include "outside_section.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using namespace mstch;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
in_section::in_section(type type, const token &start_token)
 | 
					 | 
				
			||||||
    : m_type(type), m_start_token(start_token), m_skipped_openings(0)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string in_section::render(render_context &ctx, const token &token)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  if (token.token_type() == token::type::section_close)
 | 
					 | 
				
			||||||
    if (token.name() == m_start_token.name() && m_skipped_openings == 0)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      auto &node = ctx.get_node(m_start_token.name());
 | 
					 | 
				
			||||||
      std::string out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (m_type == type::normal && !visit(is_node_empty(), node))
 | 
					 | 
				
			||||||
        out = visit(render_section(ctx, m_section, m_start_token.delims()), node);
 | 
					 | 
				
			||||||
      else if (m_type == type::inverted && visit(is_node_empty(), node))
 | 
					 | 
				
			||||||
        out = render_context::push(ctx).render(m_section);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      ctx.set_state<outside_section>();
 | 
					 | 
				
			||||||
      return out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      m_skipped_openings--;
 | 
					 | 
				
			||||||
  else if (token.token_type() == token::type::inverted_section_open || token.token_type() == token::type::section_open)
 | 
					 | 
				
			||||||
    m_skipped_openings++;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  m_section << token;
 | 
					 | 
				
			||||||
  return "";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,29 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <sstream>
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "../template_type.hpp"
 | 
					 | 
				
			||||||
#include "render_state.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class in_section : public render_state
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  enum class type
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    inverted,
 | 
					 | 
				
			||||||
    normal
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  in_section(type type, const token &start_token);
 | 
					 | 
				
			||||||
  std::string render(render_context &context, const token &token) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
  const type m_type;
 | 
					 | 
				
			||||||
  const token &m_start_token;
 | 
					 | 
				
			||||||
  template_type m_section;
 | 
					 | 
				
			||||||
  int m_skipped_openings;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,32 +0,0 @@
 | 
				
			|||||||
#include "outside_section.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "../render_context.hpp"
 | 
					 | 
				
			||||||
#include "../visitor/render_node.hpp"
 | 
					 | 
				
			||||||
#include "in_section.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using namespace mstch;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string outside_section::render(render_context &ctx, const token &token)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  using flag = render_node::flag;
 | 
					 | 
				
			||||||
  switch (token.token_type())
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
  case token::type::section_open:
 | 
					 | 
				
			||||||
    ctx.set_state<in_section>(in_section::type::normal, token);
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case token::type::inverted_section_open:
 | 
					 | 
				
			||||||
    ctx.set_state<in_section>(in_section::type::inverted, token);
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case token::type::variable:
 | 
					 | 
				
			||||||
    return visit(render_node(ctx, flag::escape_html), ctx.get_node(token.name()));
 | 
					 | 
				
			||||||
  case token::type::unescaped_variable:
 | 
					 | 
				
			||||||
    return visit(render_node(ctx, flag::none), ctx.get_node(token.name()));
 | 
					 | 
				
			||||||
  case token::type::text:
 | 
					 | 
				
			||||||
    return token.raw();
 | 
					 | 
				
			||||||
  case token::type::partial:
 | 
					 | 
				
			||||||
    return ctx.render_partial(token.name(), token.partial_prefix());
 | 
					 | 
				
			||||||
  default:
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return "";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,13 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "render_state.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class outside_section : public render_state
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  std::string render(render_context &context, const token &token) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <memory>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "../token.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class render_context;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class render_state
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  virtual ~render_state() {}
 | 
					 | 
				
			||||||
  virtual std::string render(render_context &context, const token &token) = 0;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,110 +0,0 @@
 | 
				
			|||||||
#include "template_type.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using namespace mstch;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template_type::template_type(const std::string &str, const delim_type &delims)
 | 
					 | 
				
			||||||
    : m_open(delims.first), m_close(delims.second)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  tokenize(str);
 | 
					 | 
				
			||||||
  strip_whitespace();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template_type::template_type(const std::string &str) : m_open("{{"), m_close("}}")
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  tokenize(str);
 | 
					 | 
				
			||||||
  strip_whitespace();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void template_type::process_text(citer begin, citer end)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  if (begin == end)
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  auto start = begin;
 | 
					 | 
				
			||||||
  for (auto it = begin; it != end; ++it)
 | 
					 | 
				
			||||||
    if (*it == '\n' || it == end - 1)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      m_tokens.push_back({{start, it + 1}});
 | 
					 | 
				
			||||||
      start = it + 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void template_type::tokenize(const std::string &tmp)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  citer beg = tmp.begin();
 | 
					 | 
				
			||||||
  auto npos = std::string::npos;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (std::size_t cur_pos = 0; cur_pos < tmp.size();)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    auto open_pos = tmp.find(m_open, cur_pos);
 | 
					 | 
				
			||||||
    auto close_pos = tmp.find(m_close, open_pos == npos ? open_pos : open_pos + 1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (close_pos != npos && open_pos != npos)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      if (*(beg + open_pos + m_open.size()) == '{' && *(beg + close_pos + m_close.size()) == '}')
 | 
					 | 
				
			||||||
        ++close_pos;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      process_text(beg + cur_pos, beg + open_pos);
 | 
					 | 
				
			||||||
      cur_pos = close_pos + m_close.size();
 | 
					 | 
				
			||||||
      m_tokens.push_back({{beg + open_pos, beg + close_pos + m_close.size()}, m_open.size(), m_close.size()});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (cur_pos == tmp.size())
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        m_tokens.push_back({{""}});
 | 
					 | 
				
			||||||
        m_tokens.back().eol(true);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (*(beg + open_pos + m_open.size()) == '=' && *(beg + close_pos - 1) == '=')
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        auto tok_beg = beg + open_pos + m_open.size() + 1;
 | 
					 | 
				
			||||||
        auto tok_end = beg + close_pos - 1;
 | 
					 | 
				
			||||||
        auto front_skip = first_not_ws(tok_beg, tok_end);
 | 
					 | 
				
			||||||
        auto back_skip = first_not_ws(reverse(tok_end), reverse(tok_beg));
 | 
					 | 
				
			||||||
        m_open = {front_skip, beg + tmp.find(' ', front_skip - beg)};
 | 
					 | 
				
			||||||
        m_close = {beg + tmp.rfind(' ', back_skip - beg) + 1, back_skip + 1};
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      process_text(beg + cur_pos, tmp.end());
 | 
					 | 
				
			||||||
      cur_pos = close_pos;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void template_type::strip_whitespace()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  auto line_begin = m_tokens.begin();
 | 
					 | 
				
			||||||
  bool has_tag = false, non_space = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (auto it = m_tokens.begin(); it != m_tokens.end(); ++it)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    auto type = (*it).token_type();
 | 
					 | 
				
			||||||
    if (type != token::type::text && type != token::type::variable && type != token::type::unescaped_variable)
 | 
					 | 
				
			||||||
      has_tag = true;
 | 
					 | 
				
			||||||
    else if (!(*it).ws_only())
 | 
					 | 
				
			||||||
      non_space = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if ((*it).eol())
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      if (has_tag && !non_space)
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        store_prefixes(line_begin);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        auto c = line_begin;
 | 
					 | 
				
			||||||
        for (bool end = false; !end; (*c).ws_only() ? c = m_tokens.erase(c) : ++c)
 | 
					 | 
				
			||||||
          if ((end = (*c).eol()))
 | 
					 | 
				
			||||||
            it = c - 1;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      non_space = has_tag = false;
 | 
					 | 
				
			||||||
      line_begin = it + 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void template_type::store_prefixes(std::vector<token>::iterator beg)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  for (auto cur = beg; !(*cur).eol(); ++cur)
 | 
					 | 
				
			||||||
    if ((*cur).token_type() == token::type::partial && cur != beg && (*(cur - 1)).ws_only())
 | 
					 | 
				
			||||||
      (*cur).partial_prefix((*(cur - 1)).raw());
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,31 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "token.hpp"
 | 
					 | 
				
			||||||
#include "utils.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class template_type
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  template_type() = default;
 | 
					 | 
				
			||||||
  template_type(const std::string &str);
 | 
					 | 
				
			||||||
  template_type(const std::string &str, const delim_type &delims);
 | 
					 | 
				
			||||||
  std::vector<token>::const_iterator begin() const { return m_tokens.begin(); }
 | 
					 | 
				
			||||||
  std::vector<token>::const_iterator end() const { return m_tokens.end(); }
 | 
					 | 
				
			||||||
  void operator<<(const token &token) { m_tokens.push_back(token); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
  std::vector<token> m_tokens;
 | 
					 | 
				
			||||||
  std::string m_open;
 | 
					 | 
				
			||||||
  std::string m_close;
 | 
					 | 
				
			||||||
  void strip_whitespace();
 | 
					 | 
				
			||||||
  void process_text(citer beg, citer end);
 | 
					 | 
				
			||||||
  void tokenize(const std::string &tmp);
 | 
					 | 
				
			||||||
  void store_prefixes(std::vector<token>::iterator beg);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,57 +0,0 @@
 | 
				
			|||||||
#include "token.hpp"
 | 
					 | 
				
			||||||
#include "utils.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using namespace mstch;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
token::type token::token_info(char c)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  switch (c)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
  case '>':
 | 
					 | 
				
			||||||
    return type::partial;
 | 
					 | 
				
			||||||
  case '^':
 | 
					 | 
				
			||||||
    return type::inverted_section_open;
 | 
					 | 
				
			||||||
  case '/':
 | 
					 | 
				
			||||||
    return type::section_close;
 | 
					 | 
				
			||||||
  case '&':
 | 
					 | 
				
			||||||
    return type::unescaped_variable;
 | 
					 | 
				
			||||||
  case '#':
 | 
					 | 
				
			||||||
    return type::section_open;
 | 
					 | 
				
			||||||
  case '!':
 | 
					 | 
				
			||||||
    return type::comment;
 | 
					 | 
				
			||||||
  default:
 | 
					 | 
				
			||||||
    return type::variable;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
token::token(const std::string &str, std::size_t left, std::size_t right) : m_raw(str), m_eol(false), m_ws_only(false)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  if (left != 0 && right != 0)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    if (str[left] == '=' && str[str.size() - right - 1] == '=')
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      m_type = type::delimiter_change;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else if (str[left] == '{' && str[str.size() - right - 1] == '}')
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      m_type = type::unescaped_variable;
 | 
					 | 
				
			||||||
      m_name = {first_not_ws(str.begin() + left + 1, str.end() - right),
 | 
					 | 
				
			||||||
                first_not_ws(str.rbegin() + 1 + right, str.rend() - left) + 1};
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      auto c = first_not_ws(str.begin() + left, str.end() - right);
 | 
					 | 
				
			||||||
      m_type = token_info(*c);
 | 
					 | 
				
			||||||
      if (m_type != type::variable)
 | 
					 | 
				
			||||||
        c = first_not_ws(c + 1, str.end() - right);
 | 
					 | 
				
			||||||
      m_name = {c, first_not_ws(str.rbegin() + right, str.rend() - left) + 1};
 | 
					 | 
				
			||||||
      m_delims = {{str.begin(), str.begin() + left}, {str.end() - right, str.end()}};
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  else
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    m_type = type::text;
 | 
					 | 
				
			||||||
    m_eol = (str.size() > 0 && str[str.size() - 1] == '\n');
 | 
					 | 
				
			||||||
    m_ws_only = (str.find_first_not_of(" \r\n\t") == std::string::npos);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,46 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using delim_type = std::pair<std::string, std::string>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class token
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  enum class type
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    text,
 | 
					 | 
				
			||||||
    variable,
 | 
					 | 
				
			||||||
    section_open,
 | 
					 | 
				
			||||||
    section_close,
 | 
					 | 
				
			||||||
    inverted_section_open,
 | 
					 | 
				
			||||||
    unescaped_variable,
 | 
					 | 
				
			||||||
    comment,
 | 
					 | 
				
			||||||
    partial,
 | 
					 | 
				
			||||||
    delimiter_change
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  token(const std::string &str, std::size_t left = 0, std::size_t right = 0);
 | 
					 | 
				
			||||||
  type token_type() const { return m_type; };
 | 
					 | 
				
			||||||
  const std::string &raw() const { return m_raw; };
 | 
					 | 
				
			||||||
  const std::string &name() const { return m_name; };
 | 
					 | 
				
			||||||
  const std::string &partial_prefix() const { return m_partial_prefix; };
 | 
					 | 
				
			||||||
  const delim_type &delims() const { return m_delims; };
 | 
					 | 
				
			||||||
  void partial_prefix(const std::string &p_partial_prefix) { m_partial_prefix = p_partial_prefix; };
 | 
					 | 
				
			||||||
  bool eol() const { return m_eol; }
 | 
					 | 
				
			||||||
  void eol(bool eol) { m_eol = eol; }
 | 
					 | 
				
			||||||
  bool ws_only() const { return m_ws_only; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
  type m_type;
 | 
					 | 
				
			||||||
  std::string m_name;
 | 
					 | 
				
			||||||
  std::string m_raw;
 | 
					 | 
				
			||||||
  std::string m_partial_prefix;
 | 
					 | 
				
			||||||
  delim_type m_delims;
 | 
					 | 
				
			||||||
  bool m_eol;
 | 
					 | 
				
			||||||
  bool m_ws_only;
 | 
					 | 
				
			||||||
  type token_info(char c);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,61 +0,0 @@
 | 
				
			|||||||
#include "utils.hpp"
 | 
					 | 
				
			||||||
#include "mstch/mstch.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mstch::citer mstch::first_not_ws(mstch::citer begin, mstch::citer end)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  for (auto it = begin; it != end; ++it)
 | 
					 | 
				
			||||||
    if (*it != ' ')
 | 
					 | 
				
			||||||
      return it;
 | 
					 | 
				
			||||||
  return end;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mstch::citer mstch::first_not_ws(mstch::criter begin, mstch::criter end)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  for (auto rit = begin; rit != end; ++rit)
 | 
					 | 
				
			||||||
    if (*rit != ' ')
 | 
					 | 
				
			||||||
      return --(rit.base());
 | 
					 | 
				
			||||||
  return --(end.base());
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
mstch::criter mstch::reverse(mstch::citer it) { return std::reverse_iterator<mstch::citer>(it); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::string mstch::html_escape(const std::string &str)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  if (mstch::config::escape)
 | 
					 | 
				
			||||||
    return mstch::config::escape(str);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::string out;
 | 
					 | 
				
			||||||
  citer start = str.begin();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  auto add_escape = [&out, &start](const std::string &escaped, citer &it) {
 | 
					 | 
				
			||||||
    out += std::string{start, it} + escaped;
 | 
					 | 
				
			||||||
    start = it + 1;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (auto it = str.begin(); it != str.end(); ++it)
 | 
					 | 
				
			||||||
    switch (*it)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
    case '&':
 | 
					 | 
				
			||||||
      add_escape("&", it);
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case '\'':
 | 
					 | 
				
			||||||
      add_escape("'", it);
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case '"':
 | 
					 | 
				
			||||||
      add_escape(""", it);
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case '<':
 | 
					 | 
				
			||||||
      add_escape("<", it);
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case '>':
 | 
					 | 
				
			||||||
      add_escape(">", it);
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    case '/':
 | 
					 | 
				
			||||||
      add_escape("/", it);
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    default:
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return out + std::string{start, str.end()};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,21 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <boost/variant/apply_visitor.hpp>
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using citer = std::string::const_iterator;
 | 
					 | 
				
			||||||
using criter = std::string::const_reverse_iterator;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
citer first_not_ws(citer begin, citer end);
 | 
					 | 
				
			||||||
citer first_not_ws(criter begin, criter end);
 | 
					 | 
				
			||||||
std::string html_escape(const std::string &str);
 | 
					 | 
				
			||||||
criter reverse(citer it);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template <class... Args> auto visit(Args &&... args) -> decltype(boost::apply_visitor(std::forward<Args>(args)...))
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  return boost::apply_visitor(std::forward<Args>(args)...);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,26 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <boost/variant/static_visitor.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "has_token.hpp"
 | 
					 | 
				
			||||||
#include "mstch/mstch.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class get_token : public boost::static_visitor<const mstch::node &>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  get_token(const std::string &token, const mstch::node &node) : m_token(token), m_node(node) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  template <class T> const mstch::node &operator()(const T &) const { return m_node; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const mstch::node &operator()(const map &map) const { return map.at(m_token); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const mstch::node &operator()(const std::shared_ptr<object> &object) const { return object->at(m_token); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
  const std::string &m_token;
 | 
					 | 
				
			||||||
  const mstch::node &m_node;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,24 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <boost/variant/static_visitor.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "mstch/mstch.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class has_token : public boost::static_visitor<bool>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  has_token(const std::string &token) : m_token(token) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  template <class T> bool operator()(const T &) const { return m_token == "."; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool operator()(const map &map) const { return map.count(m_token) == 1; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool operator()(const std::shared_ptr<object> &object) const { return object->has(m_token); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
  const std::string &m_token;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,27 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <boost/variant/static_visitor.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "mstch/mstch.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class is_node_empty : public boost::static_visitor<bool>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  template <class T> bool operator()(const T &) const { return false; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool operator()(const std::nullptr_t &) const { return true; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool operator()(const int &value) const { return value == 0; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool operator()(const double &value) const { return value == 0; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool operator()(const bool &value) const { return !value; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool operator()(const std::string &value) const { return value == ""; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool operator()(const array &array) const { return array.size() == 0; }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,52 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <boost/variant/static_visitor.hpp>
 | 
					 | 
				
			||||||
#include <sstream>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "../render_context.hpp"
 | 
					 | 
				
			||||||
#include "../utils.hpp"
 | 
					 | 
				
			||||||
#include "mstch/mstch.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class render_node : public boost::static_visitor<std::string>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  enum class flag
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    none,
 | 
					 | 
				
			||||||
    escape_html
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  render_node(render_context &ctx, flag p_flag = flag::none) : m_ctx(ctx), m_flag(p_flag) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  template <class T> std::string operator()(const T &) const { return ""; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::string operator()(const int &value) const { return std::to_string(value); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::string operator()(const double &value) const
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    std::stringstream ss;
 | 
					 | 
				
			||||||
    ss << value;
 | 
					 | 
				
			||||||
    return ss.str();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::string operator()(const bool &value) const { return value ? "true" : "false"; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::string operator()(const lambda &value) const
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    template_type interpreted{value([this](const mstch::node &n) { return visit(render_node(m_ctx), n); })};
 | 
					 | 
				
			||||||
    auto rendered = render_context::push(m_ctx).render(interpreted);
 | 
					 | 
				
			||||||
    return (m_flag == flag::escape_html) ? html_escape(rendered) : rendered;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::string operator()(const std::string &value) const
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    return (m_flag == flag::escape_html) ? html_escape(value) : value;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
  render_context &m_ctx;
 | 
					 | 
				
			||||||
  flag m_flag;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,58 +0,0 @@
 | 
				
			|||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <boost/variant/static_visitor.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "../render_context.hpp"
 | 
					 | 
				
			||||||
#include "../utils.hpp"
 | 
					 | 
				
			||||||
#include "mstch/mstch.hpp"
 | 
					 | 
				
			||||||
#include "render_node.hpp"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mstch
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class render_section : public boost::static_visitor<std::string>
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
  enum class flag
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    none,
 | 
					 | 
				
			||||||
    keep_array
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  render_section(render_context &ctx, const template_type §ion, const delim_type &delims, flag p_flag = flag::none)
 | 
					 | 
				
			||||||
      : m_ctx(ctx), m_section(section), m_delims(delims), m_flag(p_flag)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  template <class T> std::string operator()(const T &t) const
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    return render_context::push(m_ctx, t).render(m_section);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::string operator()(const lambda &fun) const
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    std::string section_str;
 | 
					 | 
				
			||||||
    for (auto &token : m_section)
 | 
					 | 
				
			||||||
      section_str += token.raw();
 | 
					 | 
				
			||||||
    template_type interpreted{fun([this](const mstch::node &n) { return visit(render_node(m_ctx), n); }, section_str),
 | 
					 | 
				
			||||||
                              m_delims};
 | 
					 | 
				
			||||||
    return render_context::push(m_ctx).render(interpreted);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::string operator()(const array &array) const
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    std::string out;
 | 
					 | 
				
			||||||
    if (m_flag == flag::keep_array)
 | 
					 | 
				
			||||||
      return render_context::push(m_ctx, array).render(m_section);
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      for (auto &item : array)
 | 
					 | 
				
			||||||
        out += visit(render_section(m_ctx, m_section, m_delims, flag::keep_array), item);
 | 
					 | 
				
			||||||
    return out;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
  render_context &m_ctx;
 | 
					 | 
				
			||||||
  const template_type &m_section;
 | 
					 | 
				
			||||||
  const delim_type &m_delims;
 | 
					 | 
				
			||||||
  flag m_flag;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -118,12 +118,10 @@ void StaticFileFrontend::ProcessNewMessage(const fs::path &fpath)
 | 
				
			|||||||
      std::clog << "cannot find thread with root " << rootmsgid << std::endl;
 | 
					      std::clog << "cannot find thread with root " << rootmsgid << std::endl;
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    TemplateEngine::Args_t thread_args;
 | 
					 | 
				
			||||||
    thread_args["posts"] = thread;
 | 
					 | 
				
			||||||
    if (m_TemplateEngine)
 | 
					    if (m_TemplateEngine)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      FileHandle_ptr out = OpenFile(threadFilePath, eWrite);
 | 
					      FileHandle_ptr out = OpenFile(threadFilePath, eWrite);
 | 
				
			||||||
      if (!out || !m_TemplateEngine->WriteTemplate("thread.mustache", thread_args, out))
 | 
					      if (!out || !m_TemplateEngine->WriteThreadPage(thread, out))
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        std::clog << "failed to write " << threadFilePath << std::endl;
 | 
					        std::clog << "failed to write " << threadFilePath << std::endl;
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
@@ -135,27 +133,19 @@ void StaticFileFrontend::ProcessNewMessage(const fs::path &fpath)
 | 
				
			|||||||
      uint32_t pageno = 0;
 | 
					      uint32_t pageno = 0;
 | 
				
			||||||
      while (pageno < m_Pages)
 | 
					      while (pageno < m_Pages)
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        page.clear();
 | 
					        page.threads.clear();
 | 
				
			||||||
        if (!m_MessageDB->LoadBoardPage(page, name, 10, m_Pages))
 | 
					        if (!m_MessageDB->LoadBoardPage(page, name, 10, m_Pages))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          std::clog << "cannot load board page " << pageno << " for " << name << std::endl;
 | 
					          std::clog << "cannot load board page " << pageno << " for " << name << std::endl;
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        TemplateEngine::Args_t page_args;
 | 
					 | 
				
			||||||
        page_args["group"] = name;
 | 
					 | 
				
			||||||
        page_args["threads"] = page;
 | 
					 | 
				
			||||||
        page_args["pageno"] = std::to_string(pageno);
 | 
					 | 
				
			||||||
        if (pageno)
 | 
					 | 
				
			||||||
          page_args["prev_pageno"] = std::to_string(pageno - 1);
 | 
					 | 
				
			||||||
        if (pageno + 1 < m_Pages)
 | 
					 | 
				
			||||||
          page_args["next_pageno"] = std::to_string(pageno + 1);
 | 
					 | 
				
			||||||
        fs::path boardPageFilename(name + "-" + std::to_string(pageno) + ".html");
 | 
					        fs::path boardPageFilename(name + "-" + std::to_string(pageno) + ".html");
 | 
				
			||||||
        if (m_TemplateEngine)
 | 
					        if (m_TemplateEngine)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          fs::path outfile = m_OutDir / boardPageFilename;
 | 
					          fs::path outfile = m_OutDir / boardPageFilename;
 | 
				
			||||||
          FileHandle_ptr out = OpenFile(outfile, eWrite);
 | 
					          FileHandle_ptr out = OpenFile(outfile, eWrite);
 | 
				
			||||||
          if (out)
 | 
					          if (out)
 | 
				
			||||||
            m_TemplateEngine->WriteTemplate("board.mustache", page_args, out);
 | 
					            m_TemplateEngine->WriteBoardPage(page, out);
 | 
				
			||||||
          else
 | 
					          else
 | 
				
			||||||
            std::clog << "failed to open board page " << outfile << std::endl;
 | 
					            std::clog << "failed to open board page " << outfile << std::endl;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,165 +1,92 @@
 | 
				
			|||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
#include <mstch/mstch.hpp>
 | 
					 | 
				
			||||||
#include <nntpchan/sanitize.hpp>
 | 
					#include <nntpchan/sanitize.hpp>
 | 
				
			||||||
#include <nntpchan/template_engine.hpp>
 | 
					#include <nntpchan/template_engine.hpp>
 | 
				
			||||||
#include <sstream>
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace nntpchan
 | 
					namespace nntpchan
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  struct StdTemplateEngine : public TemplateEngine
 | 
				
			||||||
template <class... Ts> struct overloaded : Ts...
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  using Ts::operator()...;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
template <class... Ts> overloaded(Ts...)->overloaded<Ts...>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace mustache = mstch;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static mustache::map post_to_map(const nntpchan::model::Post &post)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  mustache::map m;
 | 
					 | 
				
			||||||
  mustache::array attachments;
 | 
					 | 
				
			||||||
  mustache::map h;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (const auto &att : nntpchan::model::GetAttachments(post))
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    mustache::map a;
 | 
					    struct RenderContext 
 | 
				
			||||||
    a["filename"] = nntpchan::model::GetFilename(att);
 | 
					 | 
				
			||||||
    a["hexdigest"] = nntpchan::model::GetHexDigest(att);
 | 
					 | 
				
			||||||
    a["thumbnail"] = nntpchan::model::GetThumbnail(att);
 | 
					 | 
				
			||||||
    attachments.push_back(a);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (const auto &item : nntpchan::model::GetHeader(post))
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    mustache::array vals;
 | 
					 | 
				
			||||||
    for (const auto &v : item.second)
 | 
					 | 
				
			||||||
      vals.push_back(v);
 | 
					 | 
				
			||||||
    h[item.first] = vals;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  m["attachments"] = attachments;
 | 
					 | 
				
			||||||
  m["message"] = nntpchan::model::GetBody(post);
 | 
					 | 
				
			||||||
  m["header"] = h;
 | 
					 | 
				
			||||||
  return m;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static mustache::map thread_to_map(const nntpchan::model::Thread &t)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  mustache::map thread;
 | 
					 | 
				
			||||||
  mustache::array posts;
 | 
					 | 
				
			||||||
  for (const auto &post : t)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    posts.push_back(post_to_map(post));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  auto &opHeader = nntpchan::model::GetHeader(t[0]);
 | 
					 | 
				
			||||||
  thread["title"] = nntpchan::model::HeaderIFind(opHeader, "subject", "None")[0];
 | 
					 | 
				
			||||||
  thread["posts"] = posts;
 | 
					 | 
				
			||||||
  return thread;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct MustacheTemplateEngine : public TemplateEngine
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  struct Impl
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Impl(const std::map<std::string, std::string> &partials) : m_partials(partials) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool ParseTemplate(const FileHandle_ptr &in)
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      std::stringstream str;
 | 
					 | 
				
			||||||
      std::string line;
 | 
					 | 
				
			||||||
      while (std::getline(*in, line))
 | 
					 | 
				
			||||||
        str << line << "\n";
 | 
					 | 
				
			||||||
      m_tmplString = str.str();
 | 
					 | 
				
			||||||
      return in->eof();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool RenderFile(const Args_t &args, const FileHandle_ptr &out)
 | 
					      bool Load(const fs::path & path)
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      mustache::map obj;
 | 
					 | 
				
			||||||
      for (const auto &item : args)
 | 
					 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        std::visit(overloaded{[&obj, item](const nntpchan::model::Model &m) {
 | 
					        // clear out previous data
 | 
				
			||||||
                                std::visit(overloaded{[&obj, item](const nntpchan::model::BoardPage &p) {
 | 
					        m_Data.clear();
 | 
				
			||||||
                                                        mustache::array threads;
 | 
					        // open file
 | 
				
			||||||
                                                        for (const auto &thread : p)
 | 
					        std::ifstream f;
 | 
				
			||||||
                                                        {
 | 
					        f.open(path);
 | 
				
			||||||
                                                          threads.push_back(thread_to_map(thread));
 | 
					        if(f.is_open())
 | 
				
			||||||
                                                        }
 | 
					        {
 | 
				
			||||||
                                                        obj[item.first] = threads;
 | 
					          for(std::string line; std::getline(f, line, '\n');)
 | 
				
			||||||
                                                      },
 | 
					          {
 | 
				
			||||||
                                                      [&obj, item](const nntpchan::model::Thread &t) {
 | 
					            m_Data += line + "\n";
 | 
				
			||||||
                                                        obj[item.first] = thread_to_map(t);
 | 
					          }
 | 
				
			||||||
                                                      }},
 | 
					          return true;
 | 
				
			||||||
                                           m);
 | 
					        }
 | 
				
			||||||
                              },
 | 
					        else 
 | 
				
			||||||
                              [&obj, item](const std::string &str) { obj[item.first] = str; }},
 | 
					          return false;
 | 
				
			||||||
                   item.second);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      std::string str = mustache::render(m_tmplString, obj);
 | 
					      virtual bool Render(const FileHandle_ptr & out) const = 0;
 | 
				
			||||||
      out->write(str.c_str(), str.size());
 | 
					 | 
				
			||||||
      out->flush();
 | 
					 | 
				
			||||||
      return !out->fail();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string m_tmplString;
 | 
					      std::string m_Data;
 | 
				
			||||||
    const std::map<std::string, std::string> &m_partials;
 | 
					    };
 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  virtual bool WriteTemplate(const fs::path &fpath, const Args_t &args, const FileHandle_ptr &out)
 | 
					    struct BoardRenderContext : public RenderContext
 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    auto templFile = OpenFile(fpath, eRead);
 | 
					 | 
				
			||||||
    if (!templFile)
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      std::clog << "no such template at " << fpath << std::endl;
 | 
					      const nntpchan::model::BoardPage & m_Page;
 | 
				
			||||||
      return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::map<std::string, std::string> partials;
 | 
					      BoardRenderContext(const nntpchan::model::BoardPage & page) : m_Page(page) {};
 | 
				
			||||||
    if (!LoadPartials(fpath.parent_path(), partials))
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      std::clog << "failed to load partials" << std::endl;
 | 
					 | 
				
			||||||
      return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Impl impl(partials);
 | 
					      virtual bool Render(const FileHandle_ptr & out) const
 | 
				
			||||||
    if (impl.ParseTemplate(templFile))
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      return impl.RenderFile(args, out);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::clog << "failed to parse template " << fpath << std::endl;
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool LoadPartials(fs::path dir, std::map<std::string, std::string> &partials)
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    const auto partial_files = {"header", "footer"};
 | 
					 | 
				
			||||||
    for (const auto &fname : partial_files)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      auto file = OpenFile(dir / fs::path(fname + std::string(".html")), eRead);
 | 
					 | 
				
			||||||
      if (!file)
 | 
					 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        std::clog << "no such partial: " << fname << std::endl;
 | 
					        *out << m_Data;
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      std::string line;
 | 
					    };
 | 
				
			||||||
      std::stringstream input;
 | 
					
 | 
				
			||||||
      while (std::getline(*file, line))
 | 
					    struct ThreadRenderContext : public RenderContext
 | 
				
			||||||
        input << line << "\n";
 | 
					    {
 | 
				
			||||||
      partials[fname] = input.str();
 | 
					      const nntpchan::model::Thread & m_Thread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      ThreadRenderContext(const nntpchan::model::Thread & thread) : m_Thread(thread) {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      virtual bool Render(const FileHandle_ptr & out) const 
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        *out << m_Data;
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool WriteBoardPage(const nntpchan::model::BoardPage & page, const FileHandle_ptr & out)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      BoardRenderContext ctx(page);
 | 
				
			||||||
 | 
					      if(ctx.Load("board.html"))
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        return ctx.Render(out);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return true;
 | 
					
 | 
				
			||||||
  }
 | 
					    bool WriteThreadPage(const nntpchan::model::Thread & thread, const FileHandle_ptr & out)
 | 
				
			||||||
};
 | 
					    {
 | 
				
			||||||
 | 
					      ThreadRenderContext ctx(thread);
 | 
				
			||||||
 | 
					      if(ctx.Load("thread.html"))
 | 
				
			||||||
 | 
					      { 
 | 
				
			||||||
 | 
					        return ctx.Render(out);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TemplateEngine *CreateTemplateEngine(const std::string &dialect)
 | 
					TemplateEngine *CreateTemplateEngine(const std::string &dialect)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  auto d = ToLower(dialect);
 | 
					  auto d = ToLower(dialect);
 | 
				
			||||||
  if (d == "mustache")
 | 
					  if (d == "std")
 | 
				
			||||||
    return new MustacheTemplateEngine;
 | 
					    return new StdTemplateEngine;
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    return nullptr;
 | 
					    return nullptr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user