Skip to content

Commit 6df3d04

Browse files
committed
simple_dom supports .ini
1 parent 1731ba6 commit 6df3d04

4 files changed

Lines changed: 191 additions & 25 deletions

File tree

ecosystem/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ endif()
1111
# Rapidjson
1212
FetchContent_Declare(
1313
rapidjson
14+
GIT_SUBMODULES ""
1415
GIT_REPOSITORY ${PHOTON_RAPIDJSON_GIT}
1516
GIT_TAG v1.1.0
1617
PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/patches/rapidjson.patch

ecosystem/simple_dom.cpp

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <photon/common/alog.h>
1111
#include <photon/common/alog-stdstring.h>
1212
#include <photon/common/utility.h>
13+
#include <photon/common/estring.h>
1314
#include <photon/common/stream.h>
1415
#include <photon/common/retval.h>
1516
#include <photon/fs/localfs.h>
@@ -334,8 +335,82 @@ static NodeImpl* parse_yaml(char* text, size_t size, int flags) {
334335
return root;
335336
}
336337

338+
class IniNode : public DocNode<IniNode> {
339+
public:
340+
using DocNode<IniNode>::DocNode;
341+
};
342+
343+
using ini_handler = void (*)(void*, estring_view, estring_view, estring_view);
344+
static int do_parse_ini(estring_view text, ini_handler h, void* user) {
345+
int err_cnt = 0;
346+
estring_view section;
347+
for (auto line: text.split_lines()) {
348+
auto comment = line.rfind(" ;");
349+
if (comment < line.size())
350+
line = line.substr(0, comment);
351+
line = line.trim();
352+
if (line.empty() || line[0] == '#' || line[0] == ';') continue;
353+
if (line[0] == '[') {
354+
if (line.back() == ']') {
355+
section = line.substr(1, line.size() - 2).trim();
356+
} else {
357+
err_cnt++;
358+
LOG_DEBUG("section with no ending: ", line);
359+
}
360+
} else {
361+
auto eq = line.find_first_of(charset("=:"));
362+
if (eq > 0 && eq < line.size() - 1) {
363+
auto key = line.substr(0, eq).trim();
364+
auto val = line.substr(eq + 1).trim();
365+
h(user, section, key, val);
366+
} else {
367+
err_cnt++;
368+
LOG_DEBUG("ill formed kv: ", line);
369+
}
370+
}
371+
}
372+
return -err_cnt;
373+
}
374+
337375
static NodeImpl* parse_ini(char* text, size_t size, int flags) {
338-
return nullptr;
376+
struct Item {
377+
estring_view section, key, val;
378+
bool operator < (const Item& rhs) const {
379+
return section < rhs.section;
380+
}
381+
};
382+
vector<Item> ctx;
383+
auto handler = [](void* user, estring_view section,
384+
estring_view key, estring_view val) {
385+
auto ctx = (vector<Item>*)user;
386+
ctx->push_back({section, key, val});
387+
LOG_DEBUG(VALUE(section), VALUE(key), VALUE(val));
388+
};
389+
int ret = do_parse_ini({text, size}, handler, &ctx);
390+
if (ret < 0 && ctx.empty())
391+
LOG_ERROR_RETURN(-1, nullptr, "ini_parse_string_length() failed: ", ret);
392+
393+
sort(ctx.begin(), ctx.end());
394+
vector<IniNode> sections, nodes;
395+
estring_view prev_sect;
396+
auto root = new IniNode(text, flags & DOC_FREE_TEXT_ON_DESTRUCTION);
397+
for (auto& x : ctx) {
398+
if (prev_sect != x.section) {
399+
prev_sect = x.section;
400+
if (!nodes.empty() && !sections.empty()) {
401+
sections.back().set_children(std::move(nodes));
402+
assert(nodes.empty());
403+
}
404+
sections.emplace_back(x.section, str{}, root);
405+
}
406+
nodes.emplace_back(x.key, x.val, root);
407+
}
408+
if (!sections.empty()) {
409+
if (!nodes.empty())
410+
sections.back().set_children(std::move(nodes));
411+
root->set_children(std::move(sections));
412+
}
413+
return root;
339414
}
340415

341416
Node parse(char* text, size_t size, int flags) {

ecosystem/simple_dom.h

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,12 @@ class Node {
8989
Node operator[](const char* key) const { return get(key); }
9090
Node operator[](size_t i) const { return get(i); }
9191
Node get_attributes() const { return get("__attributes__"); }
92-
str to_string() const { return value(); }
92+
str to_string_view() const { return value(); }
9393
#undef IF_RET
94-
int64_t to_integer(int64_t def_val = 0) const {
94+
int64_t to_int64_t(int64_t def_val = 0) const {
9595
return value().to_int64(def_val);
9696
}
97-
double to_number(double def_val = NAN) const {
97+
double to_double(double def_val = NAN) const {
9898
return value().to_double(def_val);
9999
}
100100

@@ -105,19 +105,19 @@ class Node {
105105
bool operator>=(str rhs) const { return value() >= rhs; }
106106
bool operator> (str rhs) const { return value() > rhs; }
107107

108-
bool operator==(int64_t rhs) const { return to_integer() == rhs; }
109-
bool operator!=(int64_t rhs) const { return to_integer() != rhs; }
110-
bool operator<=(int64_t rhs) const { return to_integer() <= rhs; }
111-
bool operator< (int64_t rhs) const { return to_integer() < rhs; }
112-
bool operator>=(int64_t rhs) const { return to_integer() >= rhs; }
113-
bool operator> (int64_t rhs) const { return to_integer() > rhs; }
114-
115-
bool operator==(double rhs) const { return to_number() == rhs; }
116-
bool operator!=(double rhs) const { return to_number() != rhs; }
117-
bool operator<=(double rhs) const { return to_number() <= rhs; }
118-
bool operator< (double rhs) const { return to_number() < rhs; }
119-
bool operator>=(double rhs) const { return to_number() >= rhs; }
120-
bool operator> (double rhs) const { return to_number() > rhs; }
108+
bool operator==(int64_t rhs) const { return to_int64_t() == rhs; }
109+
bool operator!=(int64_t rhs) const { return to_int64_t() != rhs; }
110+
bool operator<=(int64_t rhs) const { return to_int64_t() <= rhs; }
111+
bool operator< (int64_t rhs) const { return to_int64_t() < rhs; }
112+
bool operator>=(int64_t rhs) const { return to_int64_t() >= rhs; }
113+
bool operator> (int64_t rhs) const { return to_int64_t() > rhs; }
114+
115+
bool operator==(double rhs) const { return to_double() == rhs; }
116+
bool operator!=(double rhs) const { return to_double() != rhs; }
117+
bool operator<=(double rhs) const { return to_double() <= rhs; }
118+
bool operator< (double rhs) const { return to_double() < rhs; }
119+
bool operator>=(double rhs) const { return to_double() >= rhs; }
120+
bool operator> (double rhs) const { return to_double() > rhs; }
121121

122122
struct SameKeyEnumerator;
123123
auto enumerable_same_key_siblings() const ->

ecosystem/test/test_simple_dom.cpp

Lines changed: 98 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ int do_list_object(string_view prefix, ObjectList& result, string* marker) {
109109
EXPECT_TRUE(key);
110110
auto size = child["Size"];
111111
EXPECT_TRUE(size);
112-
auto text = key.to_string();
113-
auto dsize = size.to_integer();
112+
auto text = key.to_string_view();
113+
auto dsize = size.to_int64_t();
114114
LOG_DEBUG(VALUE(text), VALUE(dsize));
115115
result.emplace_back(0, DT_REG, text.substr(prefix.size()),
116116
dsize, text.size() == prefix.size());
@@ -123,15 +123,15 @@ int do_list_object(string_view prefix, ObjectList& result, string* marker) {
123123
for (auto child: list_bucket_result.enumerable_children("CommonPrefixes")) {
124124
auto key = child["Prefix"];
125125
EXPECT_TRUE(key);
126-
auto dirname = key.to_string();
126+
auto dirname = key.to_string_view();
127127
if (dirname.back() == '/') dirname.remove_suffix(1);
128128
// update_stat_cache(dirname, 0, OSS_DIR_MODE);
129129
dirname.remove_prefix(prefix.size());
130130
result.emplace_back(0, DT_DIR, dirname, 0, false);
131131
}
132132
if (marker) {
133133
auto next_marker = list_bucket_result["NextMarker"];
134-
if (next_marker) *marker = next_marker.to_string();
134+
if (next_marker) *marker = next_marker.to_string_view();
135135
else marker->clear();
136136
}
137137
return 0;
@@ -153,7 +153,7 @@ void expect_eq_kvs(Node node, const char * const * truth, size_t n) {
153153
for (size_t i = 0; i < n; ++i) {
154154
auto x = truth + i * 2;
155155
auto q = node[x[0]];
156-
LOG_DEBUG("expect node['`'] => '`' (got '`')", x[0], x[1], q.to_string());
156+
LOG_DEBUG("expect node['`'] => '`' (got `)", x[0], x[1], q.to_string_view());
157157
EXPECT_EQ(q, x[1]);
158158
}
159159
}
@@ -167,7 +167,7 @@ void expect_eq_vals(Node node, const char * const * truth, size_t n) {
167167
for (size_t i = 0; i < n; ++i) {
168168
auto x = truth[i];
169169
auto q = node[i];
170-
LOG_DEBUG("expect node[`] => '`' (got '`')", i, x, q.to_string());
170+
LOG_DEBUG("expect node[`] => '`' (got '`')", i, x, q.to_string_view());
171171
EXPECT_EQ(q, x);
172172
}
173173
}
@@ -196,8 +196,8 @@ TEST(simple_dom, json) {
196196
{"i", "-123"},
197197
{"pi", "3.1416"},
198198
});
199-
EXPECT_EQ(doc["i"].to_integer(), -123);
200-
EXPECT_NEAR(doc["pi"].to_number(), 3.1416, 1e-5);
199+
EXPECT_EQ(doc["i"].to_int64_t(), -123);
200+
EXPECT_NEAR(doc["pi"].to_double(), 3.1416, 1e-5);
201201
expect_eq_vals(doc["a"], {"1", "2", "3", "4"});
202202
}
203203

@@ -241,3 +241,93 @@ I am something: indeed
241241
expect_eq_vals(doc["bar"], {"20", "30",
242242
"oh so nice", "oh so nice (serialized)"});
243243
}
244+
245+
const static char example_ini[] = R"(
246+
[protocol] ; Protocol configuration
247+
version=6 ; IPv6
248+
249+
[user]
250+
name = Bob Smith ; Spaces around '=' are stripped
251+
email = bob@smith.com ; And comments (like this) ignored
252+
active = true ; Test a boolean
253+
pi = 3.14159 ; Test a floating point number
254+
trillion = 1000000000000 ; Test 64-bit integers
255+
256+
[protocol] ; Protocol configuration
257+
ver = 4 ; IPv4
258+
259+
[section1]
260+
single1 = abc
261+
single2 = xyz
262+
[section3]
263+
single: ghi
264+
multi: the quick
265+
name = bob smith ; comment line 1
266+
; comment line 2
267+
foo = bar ;c1
268+
269+
[comment_test]
270+
test1 = 1;2;3 ; only this will be a comment
271+
test2 = 2;3;4;this won't be a comment, needs whitespace before ';'
272+
test;3 = 345 ; key should be "test;3"
273+
test4 = 4#5#6 ; '#' only starts a comment at start of line
274+
#test5 = 567 ; entire line commented
275+
# test6 = 678 ; entire line commented, except in MULTILINE mode
276+
test7 = ; blank value, except if inline comments disabled
277+
test8 =; not a comment, needs whitespace before ';'
278+
279+
[colon_tests]
280+
Content-Type: text/html
281+
foo:bar
282+
adams : 42
283+
funny1 : with = equals
284+
funny2 = with : colons
285+
funny3 = two = equals
286+
funny4 : two : colons
287+
288+
289+
)";
290+
291+
TEST(simple_dom, ini) {
292+
auto doc = parse_copy(example_ini, sizeof(example_ini) - 1, DOC_INI);
293+
EXPECT_TRUE(doc);
294+
EXPECT_EQ(doc.num_children(), 6);
295+
expect_eq_kvs(doc["protocol"], {
296+
{"version", "6"},
297+
{"ver", "4"},
298+
});
299+
expect_eq_kvs(doc["user"], {
300+
{"name", "Bob Smith"},
301+
{"email", "bob@smith.com"},
302+
{"active", "true"},
303+
{"pi", "3.14159"},
304+
{"trillion", "1000000000000"},
305+
});
306+
expect_eq_kvs(doc["section1"], {
307+
{"single1", "abc"},
308+
{"single2", "xyz"},
309+
});
310+
expect_eq_kvs(doc["section3"], {
311+
{"single", "ghi"},
312+
{"multi", "the quick"},
313+
{"name", "bob smith"},
314+
{"foo", "bar"},
315+
});
316+
expect_eq_kvs(doc["comment_test"], {
317+
{"test1", "1;2;3"},
318+
{"test2", "2;3;4;this won't be a comment, needs whitespace before ';'"},
319+
{"test;3", "345"},
320+
{"test4", "4#5#6"},
321+
{"test7", ""},
322+
{"test8", "; not a comment, needs whitespace before ';'"},
323+
});
324+
expect_eq_kvs(doc["colon_tests"], {
325+
{"Content-Type", "text/html"},
326+
{"foo", "bar"},
327+
{"adams", "42"},
328+
{"funny1", "with = equals"},
329+
{"funny2", "with : colons"},
330+
{"funny3", "two = equals"},
331+
{"funny4", "two : colons"},
332+
});
333+
}

0 commit comments

Comments
 (0)