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;
};
};
|