From c552070230c4ad9c92b516c28658b5c74c57e46b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 11 Jun 2015 18:12:09 +0200 Subject: Outline of parsing and serialization for manifest object model --- bpkg/manifest | 23 ++++++++++- bpkg/manifest-parser | 38 +++++++++--------- bpkg/manifest-parser.cxx | 2 +- bpkg/manifest.cxx | 79 +++++++++++++++++++++++++++++++++++++ tests/buildfile | 2 +- tests/manifest-parser/driver.cxx | 2 +- tests/manifest-roundtrip/driver.cxx | 4 +- tests/manifest/buildfile | 11 ++++++ tests/manifest/driver.cxx | 46 +++++++++++++++++++++ tests/manifest/manifest | 4 ++ 10 files changed, 185 insertions(+), 26 deletions(-) create mode 100644 tests/manifest/buildfile create mode 100644 tests/manifest/driver.cxx create mode 100644 tests/manifest/manifest diff --git a/bpkg/manifest b/bpkg/manifest index bf17a64..a02a4a0 100644 --- a/bpkg/manifest +++ b/bpkg/manifest @@ -12,6 +12,10 @@ namespace bpkg { + class manifest_parser; + class manifest_serializer; + class manifest_name_value; + using strings = std::vector; // priority @@ -101,8 +105,9 @@ namespace bpkg std::string comment; }; - struct manifest + class manifest { + public: using priority_type = bpkg::priority; using url_type = bpkg::url; using email_type = bpkg::email; @@ -121,9 +126,23 @@ namespace bpkg butl::optional package_email; std::vector dependencies; std::vector requirements; + + public: + manifest (manifest_parser&); + manifest (manifest_parser&, const manifest_name_value& start); + + void + serialize (manifest_serializer&) const; }; - using manifests = std::vector; + class manifests: public std::vector + { + public: + manifests (manifest_parser&); + + void + serialize (manifest_serializer&) const; + }; } #endif // BPKG_MANIFEST diff --git a/bpkg/manifest-parser b/bpkg/manifest-parser index 070cb77..6dc3ee3 100644 --- a/bpkg/manifest-parser +++ b/bpkg/manifest-parser @@ -26,6 +26,22 @@ namespace bpkg std::string description; }; + class manifest_name_value + { + public: + std::string name; + std::string value; + + std::uint64_t name_line; + std::uint64_t name_column; + + std::uint64_t value_line; + std::uint64_t value_column; + + bool + empty () const {return name.empty () && value.empty ();} + }; + class manifest_parser { public: @@ -35,22 +51,6 @@ namespace bpkg const std::string& name () const {return name_;} - class name_value_type - { - public: - std::string name; - std::string value; - - std::uint64_t name_line; - std::uint64_t name_column; - - std::uint64_t value_line; - std::uint64_t value_column; - - bool - empty () const {return name.empty () && value.empty ();} - }; - // The first returned pair is special "start-of-manifest" with // empty name and value being the format version: {"", ""}. // After that we have a sequence of ordinary pairs which are @@ -64,7 +64,7 @@ namespace bpkg // // ({"", ""} {"", ""}* {"", ""})* {"", ""} // - name_value_type + manifest_name_value next (); private: @@ -94,10 +94,10 @@ namespace bpkg private: void - parse_name (name_value_type&); + parse_name (manifest_name_value&); void - parse_value (name_value_type&); + parse_value (manifest_name_value&); // Skip spaces and return the first peeked non-space character. // diff --git a/bpkg/manifest-parser.cxx b/bpkg/manifest-parser.cxx index 037b874..f8a2e36 100644 --- a/bpkg/manifest-parser.cxx +++ b/bpkg/manifest-parser.cxx @@ -12,7 +12,7 @@ using namespace std; namespace bpkg { using parsing = manifest_parsing; - using name_value = manifest_parser::name_value_type; + using name_value = manifest_name_value; name_value manifest_parser:: next () diff --git a/bpkg/manifest.cxx b/bpkg/manifest.cxx index 40c7a32..befd3c0 100644 --- a/bpkg/manifest.cxx +++ b/bpkg/manifest.cxx @@ -4,8 +4,87 @@ #include +#include // move() + +#include +#include + using namespace std; namespace bpkg { + using parser = manifest_parser; + using parsing = manifest_parsing; + using serializer = manifest_serializer; + using name_value = manifest_name_value; + + // manifest + // + manifest:: + manifest (parser& p): manifest (p, p.next ()) // Delegate. + { + // Make sure this is the end. + // + name_value nv (p.next ()); + if (!nv.empty ()) + throw parsing (p.name (), nv.name_line, nv.name_column, + "single manifest expected"); + } + + manifest:: + manifest (parser& p, const name_value& s) + { + // Make sure this is the start and we support the version. + // + if (!s.name.empty ()) + throw parsing (p.name (), s.name_line, s.name_column, + "start of manifest expected"); + + if (s.value != "1") + throw parsing (p.name (), s.value_line, s.value_column, + "unsupported format version"); + + for (name_value nv (p.next ()); !nv.empty (); nv = p.next ()) + { + string& n (nv.name); + string& v (nv.value); + + if (n == "name") + name = move (v); + // ... + else + throw parsing (p.name (), nv.value_line, nv.value_column, + "unknown name " + n); + } + + // Verify all non-optional values were specified. + // + } + + void manifest:: + serialize (serializer& s) const + { + s.next ("", "1"); // Start of manifest. + s.next ("name", name); + // ... + s.next ("", ""); // End of manifest. + } + + // manifests + // + manifests:: + manifests (parser& p) + { + for (name_value nv (p.next ()); !nv.empty (); nv = p.next ()) + push_back (manifest (p, nv)); + } + + void manifests:: + serialize (serializer& s) const + { + for (const manifest& m: *this) + m.serialize (s); + + s.next ("", ""); // End of stream. + } } diff --git a/tests/buildfile b/tests/buildfile index d7b8b7f..1f70dd5 100644 --- a/tests/buildfile +++ b/tests/buildfile @@ -2,6 +2,6 @@ # copyright : Copyright (c) 2014-2015 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file -d = manifest-parser/ manifest-serializer/ manifest-roundtrip/ +d = manifest-parser/ manifest-serializer/ manifest-roundtrip/ manifest/ .: $d include $d diff --git a/tests/manifest-parser/driver.cxx b/tests/manifest-parser/driver.cxx index 309106f..c54e2f0 100644 --- a/tests/manifest-parser/driver.cxx +++ b/tests/manifest-parser/driver.cxx @@ -157,7 +157,7 @@ parse (const char* m) for (bool eom (true), eos (false); !eos; ) { - auto nv (p.next ()); + manifest_name_value nv (p.next ()); if (nv.empty ()) // End pair. { diff --git a/tests/manifest-roundtrip/driver.cxx b/tests/manifest-roundtrip/driver.cxx index 45abc67..f8a95bd 100644 --- a/tests/manifest-roundtrip/driver.cxx +++ b/tests/manifest-roundtrip/driver.cxx @@ -26,12 +26,12 @@ main (int argc, char* argv[]) ifs.exceptions (ifstream::badbit | ifstream::failbit); ifs.open (argv[1], ifstream::in | ifstream::binary); - manifest_parser p (ifs, ""); + manifest_parser p (ifs, argv[1]); manifest_serializer s (cout, "stdout"); for (bool eom (true), eos (false); !eos; ) { - auto nv (p.next ()); + manifest_name_value nv (p.next ()); if (nv.empty ()) // End pair. { diff --git a/tests/manifest/buildfile b/tests/manifest/buildfile new file mode 100644 index 0000000..ee23ca9 --- /dev/null +++ b/tests/manifest/buildfile @@ -0,0 +1,11 @@ +# file : tests/manifest/buildfile +# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +import libs += libbutl #@@ hack to get -I + +exe{driver}: cxx{driver} ../../bpkg/lib{bpkg} $libs + +include ../../bpkg/ + +# test: ./driver manifest | diff -u manifest - diff --git a/tests/manifest/driver.cxx b/tests/manifest/driver.cxx new file mode 100644 index 0000000..ff6005c --- /dev/null +++ b/tests/manifest/driver.cxx @@ -0,0 +1,46 @@ +// file : tests/manifest/driver.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include +#include + +#include +#include +#include + +using namespace std; +using namespace bpkg; + +int +main (int argc, char* argv[]) +{ + if (argc != 2) + { + cerr << "usage: " << argv[0] << " " << endl; + return 1; + } + + try + { + ifstream ifs; + ifs.exceptions (ifstream::badbit | ifstream::failbit); + ifs.open (argv[1], ifstream::in | ifstream::binary); + + manifest_parser p (ifs, argv[1]); + manifests ms (p); + + manifest_serializer s (cout, "stdout"); + ms.serialize (s); + } + catch (const ios_base::failure&) + { + cerr << "io failure" << endl; + return 1; + } + catch (const std::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/tests/manifest/manifest b/tests/manifest/manifest new file mode 100644 index 0000000..25491ef --- /dev/null +++ b/tests/manifest/manifest @@ -0,0 +1,4 @@ +: 1 +name: libfoo +: +name: libbar -- cgit v1.1