summaryrefslogtreecommitdiff
path: root/includes/SimpleMustache.h
blob: f30c831703f19ac8fdd65516161e8dd25afec639 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#pragma once
#include <map>
#include <vector>
#include <list>
#include <Handle.h>
#include <Buffer.h>

namespace Balau {

class SimpleMustache {
  public:

    class Context {
      public:
        class Proxy {
          public:
            Context & operator[](const char * str);
          private:
              Proxy(Context * parent, ssize_t idx) : m_parent(parent), m_idx(idx) { }
            Context * m_parent = NULL;
            ssize_t m_idx;
            friend class Context;
        };

          Context() { }
          ~Context() { empty(); }
        Proxy operator[](ssize_t idx) { ensureList(); return Proxy(this, idx); }
        Context & operator[](const char * str);
        // we should try and support lambdas, but I'm not entierely sure about them.
        // Something tells me they need some design love, especially about which
        // context they use. The specification says they should expand the tags,
        // but the example doesn't show a function that'd take a context...
        Context & operator=(const String & str) {
            empty();
            m_type = STRING;
            m_str = str;
            return *this;
        }
        Context & operator=(const char * str) {
            empty();
            m_type = STRING;
            m_str = str;
            return *this;
        }
        Context & operator=(bool b) {
            empty();
            m_type = BOOLSEC;
            m_bool = b;
            return *this;
        }
      private:
        enum ContextType {
            UNKNOWN,
            STRING,
            BOOLSEC,
            CONTEXTLIST,
            LAMBDA,
        } m_type = CONTEXTLIST;
          Context(ContextType type) : m_type(type), m_root(false) { }
        String m_str;
        bool m_bool;
        typedef std::map<String, Context *> SubContext;
        typedef std::vector<SubContext> ContextList;
        ContextList m_contextList;
        bool m_root = true;

        void empty(bool skipFirst = false);
        void ensureList(bool single = false);

        friend class Proxy;
        friend class SimpleMustache;

          Context(const Context &) = delete;
        Context & operator=(const Context &) = delete;
    };

    void setTemplate(IO<Handle> h);
    void setTemplate(const uint8_t * str, ssize_t s = -1) {
        if (s < 0)
            s = strlen((const char *) str);
        IO<Buffer> b(new Buffer(str, s));
        setTemplate(b);
    }
    template<size_t S>
    void setTemplate(const char (&str)[S]) { setTemplate((const uint8_t *) str, S - 1); }
    void setTemplate(const char * str, ssize_t s) { setTemplate((const uint8_t *) str, s); }
    void setTemplate(const String & str) { setTemplate((const uint8_t *) str.to_charp(), str.strlen()); }
    void render(IO<Handle> h, Context * ctx) const { AAssert(ctx, "Please pass on a context to render"); render_r(h, ctx, "", m_fragments.begin(), false, -1); }
    void empty() { while (!m_fragments.empty()) { delete m_fragments.front(); m_fragments.pop_front(); } }
    void checkTemplate() { Fragments::const_iterator end = checkTemplate_r(m_fragments.begin()); AAssert(end == m_fragments.end(), "The template wasn't fully checked; possibly mismatched sections"); }
      SimpleMustache() { }
      ~SimpleMustache() { empty(); }
  private:
    struct Fragment {
        enum {
            UNKNOWN,
            STRING,
            VARIABLE,
            NOESCAPE,
            SECTION,
            INVERTED,
            END_SECTION,
        } type;
        String str; // contains either the string, the variable name, or the sections names.
    };
    typedef std::list<Fragment *> Fragments;
    Fragments m_fragments;

    Fragments::const_iterator render_r(IO<Handle> h, Context * ctx, const String & endSection, Fragments::const_iterator begin, bool noWrite, int forceIdx) const;
    static String escape(const String & s);

    Fragments::const_iterator checkTemplate_r(Fragments::const_iterator begin, const String & endSection = "") const;

      SimpleMustache(const SimpleMustache &) = delete;
    SimpleMustache & operator=(const SimpleMustache &) = delete;
};

};