Fancy printer predicate now takes current JSON object

The API is ugly at the moment. Will fix with an ugly
implementation soon.
This commit is contained in:
Evan Driscoll 2018-06-04 21:59:21 -05:00
parent c7a64b8846
commit edb6b25569
3 changed files with 147 additions and 38 deletions

View File

@ -43,19 +43,31 @@ struct fancy_serializer_style
bool multiline = false; bool multiline = false;
void set_old_multiline() fancy_serializer_style() = default;
{
space_after_colon = space_after_comma = multiline = true; fancy_serializer_style(bool s_colon, bool s_comma, bool ml)
} : space_after_colon(s_colon), space_after_comma(s_comma), multiline(ml)
{}
static const fancy_serializer_style preset_compact;
static const fancy_serializer_style preset_one_line;
static const fancy_serializer_style preset_multiline;
}; };
const fancy_serializer_style fancy_serializer_style::preset_compact(false, false, false);
const fancy_serializer_style fancy_serializer_style::preset_one_line(true, true, false);
const fancy_serializer_style fancy_serializer_style::preset_multiline(true, true, true);
template<typename BasicJsonType> template<typename BasicJsonType>
class basic_fancy_serializer_stylizer class basic_fancy_serializer_stylizer
{ {
public: public:
using string_t = typename BasicJsonType::string_t; using string_t = typename BasicJsonType::string_t;
using json_pointer_t = json_pointer<BasicJsonType>; using json_pointer_t = json_pointer<BasicJsonType>;
using matcher_predicate = std::function<bool (const json_pointer_t&)>;
using json_matcher_predicate = std::function<bool (const BasicJsonType&)>;
using context_matcher_predicate = std::function<bool (const json_pointer_t&)>;
using matcher_predicate = std::function<bool (const json_pointer_t&, const BasicJsonType&)>;
basic_fancy_serializer_stylizer(fancy_serializer_style const& ds) basic_fancy_serializer_stylizer(fancy_serializer_style const& ds)
: default_style(ds) : default_style(ds)
@ -76,11 +88,12 @@ class basic_fancy_serializer_stylizer
const fancy_serializer_style* get_new_style_or_active( const fancy_serializer_style* get_new_style_or_active(
const json_pointer_t& pointer, const json_pointer_t& pointer,
const json& j,
const fancy_serializer_style* active_style) const const fancy_serializer_style* active_style) const
{ {
for (auto const& pair : styles) for (auto const& pair : styles)
{ {
if (pair.first(pointer)) if (pair.first(pointer, j))
{ {
return &pair.second; return &pair.second;
} }
@ -96,15 +109,40 @@ class basic_fancy_serializer_stylizer
return styles.back().second; return styles.back().second;
} }
fancy_serializer_style& register_style(
json_matcher_predicate p,
fancy_serializer_style style = fancy_serializer_style())
{
auto wrapper = [p](const json_pointer_t&, const BasicJsonType & j)
{
return p(j);
};
styles.emplace_back(wrapper, style);
return styles.back().second;
}
fancy_serializer_style& register_style(
context_matcher_predicate p,
fancy_serializer_style style = fancy_serializer_style())
{
auto wrapper = [p](const json_pointer_t& c, const BasicJsonType&)
{
return p(c);
};
styles.emplace_back(wrapper, style);
return styles.back().second;
}
fancy_serializer_style& register_key_matcher_style( fancy_serializer_style& register_key_matcher_style(
string_t str, string_t str,
fancy_serializer_style style = fancy_serializer_style()) fancy_serializer_style style = fancy_serializer_style())
{ {
return register_style([str](const json_pointer_t& pointer) using pred = context_matcher_predicate;
return register_style(pred([str](const json_pointer_t& pointer)
{ {
return (pointer.cbegin() != pointer.cend()) return (pointer.cbegin() != pointer.cend())
&& (*pointer.crbegin() == str); && (*pointer.crbegin() == str);
}, }),
style); style);
} }
@ -180,7 +218,7 @@ class fancy_serializer
const fancy_serializer_style* active_style, const fancy_serializer_style* active_style,
const json_pointer_t& context) const json_pointer_t& context)
{ {
active_style = stylizer.get_new_style_or_active(context, active_style); active_style = stylizer.get_new_style_or_active(context, val, active_style);
switch (val.m_type) switch (val.m_type)
{ {

View File

@ -10848,19 +10848,31 @@ struct fancy_serializer_style
bool multiline = false; bool multiline = false;
void set_old_multiline() fancy_serializer_style() = default;
{
space_after_colon = space_after_comma = multiline = true; fancy_serializer_style(bool s_colon, bool s_comma, bool ml)
} : space_after_colon(s_colon), space_after_comma(s_comma), multiline(ml)
{}
static const fancy_serializer_style preset_compact;
static const fancy_serializer_style preset_one_line;
static const fancy_serializer_style preset_multiline;
}; };
const fancy_serializer_style fancy_serializer_style::preset_compact(false, false, false);
const fancy_serializer_style fancy_serializer_style::preset_one_line(true, true, false);
const fancy_serializer_style fancy_serializer_style::preset_multiline(true, true, true);
template<typename BasicJsonType> template<typename BasicJsonType>
class basic_fancy_serializer_stylizer class basic_fancy_serializer_stylizer
{ {
public: public:
using string_t = typename BasicJsonType::string_t; using string_t = typename BasicJsonType::string_t;
using json_pointer_t = json_pointer<BasicJsonType>; using json_pointer_t = json_pointer<BasicJsonType>;
using matcher_predicate = std::function<bool (const json_pointer_t&)>;
using json_matcher_predicate = std::function<bool (const BasicJsonType&)>;
using context_matcher_predicate = std::function<bool (const json_pointer_t&)>;
using matcher_predicate = std::function<bool (const json_pointer_t&, const BasicJsonType&)>;
basic_fancy_serializer_stylizer(fancy_serializer_style const& ds) basic_fancy_serializer_stylizer(fancy_serializer_style const& ds)
: default_style(ds) : default_style(ds)
@ -10881,11 +10893,12 @@ class basic_fancy_serializer_stylizer
const fancy_serializer_style* get_new_style_or_active( const fancy_serializer_style* get_new_style_or_active(
const json_pointer_t& pointer, const json_pointer_t& pointer,
const json& j,
const fancy_serializer_style* active_style) const const fancy_serializer_style* active_style) const
{ {
for (auto const& pair : styles) for (auto const& pair : styles)
{ {
if (pair.first(pointer)) if (pair.first(pointer, j))
{ {
return &pair.second; return &pair.second;
} }
@ -10901,15 +10914,40 @@ class basic_fancy_serializer_stylizer
return styles.back().second; return styles.back().second;
} }
fancy_serializer_style& register_style(
json_matcher_predicate p,
fancy_serializer_style style = fancy_serializer_style())
{
auto wrapper = [p](const json_pointer_t&, const BasicJsonType & j)
{
return p(j);
};
styles.emplace_back(wrapper, style);
return styles.back().second;
}
fancy_serializer_style& register_style(
context_matcher_predicate p,
fancy_serializer_style style = fancy_serializer_style())
{
auto wrapper = [p](const json_pointer_t& c, const BasicJsonType&)
{
return p(c);
};
styles.emplace_back(wrapper, style);
return styles.back().second;
}
fancy_serializer_style& register_key_matcher_style( fancy_serializer_style& register_key_matcher_style(
string_t str, string_t str,
fancy_serializer_style style = fancy_serializer_style()) fancy_serializer_style style = fancy_serializer_style())
{ {
return register_style([str](const json_pointer_t& pointer) using pred = context_matcher_predicate;
return register_style(pred([str](const json_pointer_t& pointer)
{ {
return (pointer.cbegin() != pointer.cend()) return (pointer.cbegin() != pointer.cend())
&& (*pointer.crbegin() == str); && (*pointer.crbegin() == str);
}, }),
style); style);
} }
@ -10985,7 +11023,7 @@ class fancy_serializer
const fancy_serializer_style* active_style, const fancy_serializer_style* active_style,
const json_pointer_t& context) const json_pointer_t& context)
{ {
active_style = stylizer.get_new_style_or_active(context, active_style); active_style = stylizer.get_new_style_or_active(context, val, active_style);
switch (val.m_type) switch (val.m_type)
{ {

View File

@ -203,7 +203,8 @@ TEST_CASE("serialization")
auto str_flat = fancy_to_string({1, {1}}, style); auto str_flat = fancy_to_string({1, {1}}, style);
CHECK(str_flat == "[1,[...]]"); CHECK(str_flat == "[1,[...]]");
style.set_old_multiline(); style = fancy_serializer_style::preset_multiline;
style.depth_limit = 1;
auto str_lines = fancy_to_string({1, {1}}, style); auto str_lines = fancy_to_string({1, {1}}, style);
CHECK(str_lines == dedent(R"( CHECK(str_lines == dedent(R"(
[ [
@ -220,7 +221,8 @@ TEST_CASE("serialization")
auto str_flat = fancy_to_string({1, {{"one", 1}}}, style); auto str_flat = fancy_to_string({1, {{"one", 1}}}, style);
CHECK(str_flat == "[1,{...}]"); CHECK(str_flat == "[1,{...}]");
style.set_old_multiline(); style = fancy_serializer_style::preset_multiline;
style.depth_limit = 1;
auto str_lines = fancy_to_string({1, {{"one", 1}}}, style); auto str_lines = fancy_to_string({1, {{"one", 1}}}, style);
CHECK(str_lines == dedent(R"( CHECK(str_lines == dedent(R"(
[ [
@ -235,8 +237,8 @@ TEST_CASE("serialization")
SECTION("can style objects of a key differently") SECTION("can style objects of a key differently")
{ {
fancy_serializer_stylizer stylizer; fancy_serializer_stylizer stylizer;
stylizer.get_default_style().set_old_multiline(); stylizer.get_default_style() = fancy_serializer_style::preset_multiline;
stylizer.register_key_matcher_style("one line").multiline = false; stylizer.register_key_matcher_style("one line");
auto str = fancy_to_string( auto str = fancy_to_string(
{ {
@ -262,8 +264,8 @@ TEST_CASE("serialization")
SECTION("changes propagate (unless overridden)") SECTION("changes propagate (unless overridden)")
{ {
fancy_serializer_stylizer stylizer; fancy_serializer_stylizer stylizer;
stylizer.get_default_style().set_old_multiline(); stylizer.get_default_style() = fancy_serializer_style::preset_multiline;
stylizer.register_key_matcher_style("one line").indent_step = 0; stylizer.register_key_matcher_style("one line");
auto str = fancy_to_string( auto str = fancy_to_string(
{ {
@ -279,18 +281,20 @@ TEST_CASE("serialization")
})")); })"));
} }
SECTION("example of more sophisticated matcher") SECTION("example of more sophisticated context matcher")
{ {
using pred = fancy_serializer_stylizer::context_matcher_predicate;
fancy_serializer_stylizer stylizer; fancy_serializer_stylizer stylizer;
stylizer.get_default_style().set_old_multiline(); stylizer.get_default_style() = fancy_serializer_style::preset_multiline;
stylizer.register_style( stylizer.register_style(
[] (const json_pointer<json>& context) pred([] (const json_pointer<json>& context)
{ {
// Matches if context[-2] is "each elem on one line" // Matches if context[-2] is "each elem on one line"
return (context.cend() - context.cbegin() >= 2) return (context.cend() - context.cbegin() >= 2)
&& (*(context.cend() - 2) == "each elem on one line"); && (*(context.cend() - 2) == "each elem on one line");
} })
).space_after_comma = true; ).space_after_comma = true;
auto str = fancy_to_string( auto str = fancy_to_string(
@ -324,6 +328,40 @@ TEST_CASE("serialization")
] ]
})")); })"));
} }
SECTION("example of more sophisticated json matcher")
{
using pred = fancy_serializer_stylizer::json_matcher_predicate;
fancy_serializer_stylizer stylizer;
stylizer.get_default_style() = fancy_serializer_style::preset_multiline;
stylizer.register_style(
pred([] (const json & j)
{
return j.type() == json::value_t::array;
})
) = fancy_serializer_style::preset_one_line;
auto str = fancy_to_string(
{
{
"an array", {1, 2, 3}
},
{
"an object", {{"key", "val"}}
}
},
stylizer);
CHECK(str == dedent(R"(
{
"an array": [1, 2, 3],
"an object": {
"key": "val"
}
})"));
}
} }
SECTION("Spaces after commas are controllable separately from multiline") SECTION("Spaces after commas are controllable separately from multiline")
@ -346,8 +384,7 @@ TEST_CASE("serialization")
SECTION("multiline can have no space") SECTION("multiline can have no space")
{ {
fancy_serializer_style style; fancy_serializer_style style = fancy_serializer_style::preset_multiline;
style.set_old_multiline();
style.space_after_colon = false; style.space_after_colon = false;
auto str = fancy_to_string({{"one", 1}}, style); auto str = fancy_to_string({{"one", 1}}, style);
CHECK(str == dedent(R"( CHECK(str == dedent(R"(
@ -360,8 +397,7 @@ TEST_CASE("serialization")
SECTION("given width") SECTION("given width")
{ {
fancy_serializer_style style; fancy_serializer_style style = fancy_serializer_style::preset_multiline;
style.set_old_multiline();
auto str = fancy_to_string({"foo", 1, 2, 3, false, {{"one", 1}}}, style); auto str = fancy_to_string({"foo", 1, 2, 3, false, {{"one", 1}}}, style);
CHECK(str == dedent(R"( CHECK(str == dedent(R"(
[ [
@ -378,10 +414,9 @@ TEST_CASE("serialization")
SECTION("given fill") SECTION("given fill")
{ {
fancy_serializer_style style; fancy_serializer_style style = fancy_serializer_style::preset_multiline;
style.indent_step = 1; style.indent_step = 1;
style.indent_char = '\t'; style.indent_char = '\t';
style.set_old_multiline();
auto str = fancy_to_string({"foo", 1, 2, 3, false, {{"one", 1}}}, style); auto str = fancy_to_string({"foo", 1, 2, 3, false, {{"one", 1}}}, style);
CHECK(str == CHECK(str ==
@ -400,10 +435,9 @@ TEST_CASE("serialization")
SECTION("indent_char is honored for deep indents in lists") SECTION("indent_char is honored for deep indents in lists")
{ {
fancy_serializer_style style; fancy_serializer_style style = fancy_serializer_style::preset_multiline;
style.indent_step = 300; style.indent_step = 300;
style.indent_char = 'X'; style.indent_char = 'X';
style.set_old_multiline();
auto str = fancy_to_string({1, {1}}, style); auto str = fancy_to_string({1, {1}}, style);
@ -419,10 +453,9 @@ TEST_CASE("serialization")
SECTION("indent_char is honored for deep indents in objects") SECTION("indent_char is honored for deep indents in objects")
{ {
fancy_serializer_style style; fancy_serializer_style style = fancy_serializer_style::preset_multiline;
style.indent_step = 300; style.indent_step = 300;
style.indent_char = 'X'; style.indent_char = 'X';
style.set_old_multiline();
auto str = fancy_to_string({{"key", {{"key", 1}}}}, style); auto str = fancy_to_string({{"key", {{"key", 1}}}}, style);