From 6ac4f3bcc0bd1306bf1a8dd1bebae1a00081c6b7 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Tue, 8 Jan 2019 15:30:40 +0300 Subject: Add support for filtering during manifest parsing and serialization --- libbutl/manifest-parser.cxx | 25 +++++++++++++++---------- libbutl/manifest-parser.ixx | 14 ++++++++++++++ libbutl/manifest-parser.mxx | 28 +++++++++++++++++++++++----- libbutl/manifest-serializer.cxx | 2 +- libbutl/manifest-serializer.ixx | 13 +++++++++++++ libbutl/manifest-serializer.mxx | 30 ++++++++++++++++++++++++++---- tests/manifest-parser/driver.cxx | 35 ++++++++++++++++++++++++++++------- tests/manifest-serializer/driver.cxx | 18 +++++++++++++----- 8 files changed, 133 insertions(+), 32 deletions(-) create mode 100644 libbutl/manifest-parser.ixx create mode 100644 libbutl/manifest-serializer.ixx diff --git a/libbutl/manifest-parser.cxx b/libbutl/manifest-parser.cxx index 20932e3..9aa35b7 100644 --- a/libbutl/manifest-parser.cxx +++ b/libbutl/manifest-parser.cxx @@ -41,13 +41,17 @@ namespace butl using parsing = manifest_parsing; using name_value = manifest_name_value; - name_value manifest_parser:: - next () + void manifest_parser:: + parse_next (name_value& r) { if (s_ == end) - return name_value { + { + r = name_value { "", "", line, column, line, column, position, position, position}; + return; + } + auto clp (skip_spaces ()); xchar c (clp.first); uint64_t start_pos (clp.second); @@ -67,15 +71,18 @@ namespace butl { s_ = start; - return name_value {"", "", - c.line, c.column, c.line, c.column, - start_pos, c.position, c.position}; + r = name_value {"", "", + c.line, c.column, c.line, c.column, + start_pos, c.position, c.position}; + return; } + r.name.clear (); + r.value.clear (); + // Regardless of the state, what should come next is a name, // potentially the special empty one. // - name_value r; r.start_pos = start_pos; parse_name (r); @@ -98,7 +105,7 @@ namespace butl r.value_column = r.name_column; r.colon_pos = r.start_pos; r.end_pos = r.start_pos; - return r; + return; } if (c != ':') @@ -162,8 +169,6 @@ namespace butl // assert (!r.name.empty ()); } - - return r; } pair manifest_parser:: diff --git a/libbutl/manifest-parser.ixx b/libbutl/manifest-parser.ixx new file mode 100644 index 0000000..b308264 --- /dev/null +++ b/libbutl/manifest-parser.ixx @@ -0,0 +1,14 @@ +// file : libbutl/manifest-parser.ixx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +namespace butl +{ + inline manifest_name_value manifest_parser:: + next () + { + manifest_name_value r; + do { parse_next (r); } while (filter_ && !filter_ (r)); + return r; + } +} diff --git a/libbutl/manifest-parser.mxx b/libbutl/manifest-parser.mxx index 7fc4ee3..0289d6f 100644 --- a/libbutl/manifest-parser.mxx +++ b/libbutl/manifest-parser.mxx @@ -11,9 +11,10 @@ #ifndef __cpp_lib_modules #include #include -#include // uint64_t -#include // pair -#include // runtime_error +#include // uint64_t +#include // pair, move() +#include // runtime_error +#include #endif // Other includes. @@ -52,8 +53,19 @@ LIBBUTL_MODEXPORT namespace butl class LIBBUTL_SYMEXPORT manifest_parser: protected butl::char_scanner { public: - manifest_parser (std::istream& is, const std::string& name) - : char_scanner (is), name_ (name) {} + // The filter, if specified, is called by next() prior to returning the + // pair to the caller. If the filter returns false, then the pair is + // discarded. + // + // Note that the filter should handle the end-of-manifest pairs (see + // below) carefully, so next() doesn't end up with an infinite cycle. + // + using filter_function = bool (manifest_name_value&); + + manifest_parser (std::istream& is, + const std::string& name, + std::function filter = {}) + : char_scanner (is), name_ (name), filter_ (std::move (filter)) {} const std::string& name () const {return name_;} @@ -83,6 +95,9 @@ LIBBUTL_MODEXPORT namespace butl private: void + parse_next (manifest_name_value&); + + void parse_name (manifest_name_value&); void @@ -98,8 +113,11 @@ LIBBUTL_MODEXPORT namespace butl private: const std::string name_; + const std::function filter_; enum {start, body, end} s_ = start; std::string version_; // Current format version. }; } + +#include diff --git a/libbutl/manifest-serializer.cxx b/libbutl/manifest-serializer.cxx index 2d2e722..dfbd9e1 100644 --- a/libbutl/manifest-serializer.cxx +++ b/libbutl/manifest-serializer.cxx @@ -39,7 +39,7 @@ namespace butl using serialization = manifest_serialization; void manifest_serializer:: - next (const string& n, const string& v) + write_next (const string& n, const string& v) { switch (s_) { diff --git a/libbutl/manifest-serializer.ixx b/libbutl/manifest-serializer.ixx new file mode 100644 index 0000000..1108ba1 --- /dev/null +++ b/libbutl/manifest-serializer.ixx @@ -0,0 +1,13 @@ +// file : libbutl/manifest-serializer.ixx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +namespace butl +{ + inline void manifest_serializer:: + next (const std::string& n, const std::string& v) + { + if (!filter_ || filter_ (n, v)) + write_next (n, v); + } +} diff --git a/libbutl/manifest-serializer.mxx b/libbutl/manifest-serializer.mxx index 66ca398..6a87656 100644 --- a/libbutl/manifest-serializer.mxx +++ b/libbutl/manifest-serializer.mxx @@ -11,8 +11,9 @@ #ifndef __cpp_lib_modules #include #include -#include // size_t -#include // runtime_error +#include // size_t +#include // runtime_error +#include #endif // Other includes. @@ -42,8 +43,23 @@ LIBBUTL_MODEXPORT namespace butl class LIBBUTL_SYMEXPORT manifest_serializer { public: - manifest_serializer (std::ostream& os, const std::string& name) - : os_ (os), name_ (name) {} + // The filter, if specified, is called by next() prior to serializing the + // pair into the stream. If the filter returns false, then the pair is + // discarded. + // + // Note that currently there is no way for the filter to modify the name + // or value. If we ever need this functionality, then we can add an + // "extended" filter alternative with two "receiving" arguments: + // + // bool (..., optional& n, optional& v); + // + using filter_function = bool (const std::string& name, + const std::string& value); + + manifest_serializer (std::ostream& os, + const std::string& name, + std::function filter = {}) + : os_ (os), name_ (name), filter_ (std::move (filter)) {} const std::string& name () const {return name_;} @@ -77,6 +93,9 @@ LIBBUTL_MODEXPORT namespace butl private: friend class manifest_rewriter; + void + write_next (const std::string& name, const std::string& value); + // Validate and write a name. // void @@ -105,5 +124,8 @@ LIBBUTL_MODEXPORT namespace butl private: std::ostream& os_; const std::string name_; + const std::function filter_; }; } + +#include diff --git a/tests/manifest-parser/driver.cxx b/tests/manifest-parser/driver.cxx index 30b754f..d2d54e9 100644 --- a/tests/manifest-parser/driver.cxx +++ b/tests/manifest-parser/driver.cxx @@ -7,7 +7,7 @@ #ifndef __cpp_lib_modules #include #include -#include // pair +#include // pair, move() #include #include #endif @@ -30,7 +30,9 @@ using namespace butl; using pairs = vector>; static bool -test (const char* manifest, const pairs& expected); +test (const char* manifest, + const pairs& expected, + manifest_parser::filter_function = {}); static bool fail (const char* manifest); @@ -160,6 +162,25 @@ main () auto p (manifest_parser::split_comment ("; comment")); assert (p.first == "" && p.second == "comment"); } + + // Filtering. + // + assert (test (":1\na: abc\nb: bca\nc: cab", + {{"","1"},{"a","abc"},{"c","cab"},{"",""},{"",""}}, + [] (manifest_name_value& nv) {return nv.name != "b";})); + + assert (test (":1\na: abc\nb: bca", + {{"","1"},{"ax","abc."},{"bx","bca."},{"",""},{"",""}}, + [] (manifest_name_value& nv) + { + if (!nv.name.empty ()) + { + nv.name += 'x'; + nv.value += '.'; + } + + return true; + })); } static ostream& @@ -177,11 +198,11 @@ operator<< (ostream& os, const pairs& ps) } static pairs -parse (const char* m) +parse (const char* m, manifest_parser::filter_function f = {}) { istringstream is (m); is.exceptions (istream::failbit | istream::badbit); - manifest_parser p (is, ""); + manifest_parser p (is, "", move (f)); pairs r; @@ -197,16 +218,16 @@ parse (const char* m) else eom = false; - r.emplace_back (nv.name, nv.value); // move + r.emplace_back (move (nv.name), move (nv.value)); } return r; } static bool -test (const char* m, const pairs& e) +test (const char* m, const pairs& e, manifest_parser::filter_function f) { - pairs r (parse (m)); + pairs r (parse (m, move (f))); if (r != e) { diff --git a/tests/manifest-serializer/driver.cxx b/tests/manifest-serializer/driver.cxx index d9a7255..be00106 100644 --- a/tests/manifest-serializer/driver.cxx +++ b/tests/manifest-serializer/driver.cxx @@ -30,7 +30,9 @@ using namespace butl; using pairs = vector>; static bool -test (const pairs& manifest, const string& expected); +test (const pairs& manifest, + const string& expected, + manifest_serializer::filter_function f = {}); static bool fail (const pairs& manifest); @@ -234,14 +236,20 @@ main () assert (manifest_serializer::merge_comment ("value text", "") == "value text"); + + // Filtering. + // + assert (test ({{"","1"},{"a","abc"},{"b","bca"},{"c","cab"},{"",""},{"",""}}, + ": 1\na: abc\nc: cab\n", + [] (const string& n, const string&) {return n != "b";})); } static string -serialize (const pairs& m) +serialize (const pairs& m, manifest_serializer::filter_function f = {}) { ostringstream os; os.exceptions (istream::failbit | istream::badbit); - manifest_serializer s (os, ""); + manifest_serializer s (os, "", f); for (const auto& p: m) { @@ -255,9 +263,9 @@ serialize (const pairs& m) } static bool -test (const pairs& m, const string& e) +test (const pairs& m, const string& e, manifest_serializer::filter_function f) { - string r (serialize (m)); + string r (serialize (m, f)); if (r != e) { -- cgit v1.1