From b39e27a9f1ab04cb67ffad1d2c7e7ae089d5e8c4 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 23 May 2018 00:36:15 +0300 Subject: Add package_name class --- libbpkg/manifest.cxx | 63 +++------------ libbpkg/manifest.hxx | 13 +--- libbpkg/package-name.cxx | 59 ++++++++++++++ libbpkg/package-name.hxx | 194 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 266 insertions(+), 63 deletions(-) create mode 100644 libbpkg/package-name.cxx create mode 100644 libbpkg/package-name.hxx diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx index 3e9a4fa..b7c3a75 100644 --- a/libbpkg/manifest.cxx +++ b/libbpkg/manifest.cxx @@ -17,7 +17,7 @@ #include #include #include // casecmp(), lcase(), alpha(), - // alnum(), digit(), xdigit() + // digit(), xdigit() #include #include #include @@ -870,44 +870,6 @@ namespace bpkg return o; } - // Package name. - // - static const strings illegal_pkg_names ({ - "build", - "con", "prn", "aux", "nul", - "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", - "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9"}); - - static const string legal_pkg_chars ("_+-."); - - void - validate_package_name (const string& name) - { - if (name.size () < 2) - throw invalid_argument ("length is less than two characters"); - - if (find (illegal_pkg_names.begin (), illegal_pkg_names.end (), name) != - illegal_pkg_names.end ()) - throw invalid_argument ("illegal name"); - - if (!alpha (name.front ())) - throw invalid_argument ("illegal first character (must be alphabetic)"); - - // Here we rely on the fact that the name length >= 2. - // - for (auto i (name.cbegin () + 1), e (name.cend () - 1); i != e; ++i) - { - char c (*i); - - if (!(alnum(c) || legal_pkg_chars.find (c) != string::npos)) - throw invalid_argument ("illegal character"); - } - - if (!alnum (name.back ())) - throw invalid_argument ( - "illegal last character (must be alphabetic or digit)"); - } - // pkg_package_manifest // static void @@ -986,14 +948,12 @@ namespace bpkg try { - validate_package_name (v); + m.name = package_name (move (v)); } catch (const invalid_argument& e) { bad_value (string ("invalid package name: ") + e.what ()); } - - m.name = move (v); } else if (n == "version") { @@ -1266,11 +1226,11 @@ namespace bpkg ne = i + 1; } - string nm (i == e ? move (lv) : string (b, ne)); + package_name nm; try { - validate_package_name (nm); + nm = package_name (i == e ? move (lv) : string (b, ne)); } catch (const invalid_argument& e) { @@ -1419,16 +1379,10 @@ namespace bpkg auto bad_value ([&s](const string& d) { throw serialization (s.name (), d);}); - try - { - validate_package_name (name); - } - catch (const invalid_argument& e) - { - bad_value (string ("invalid package name: ") + e.what ()); - } + if (name.empty ()) + bad_value ("empty package name"); - s.next ("name", name); + s.next ("name", name.string ()); s.next ("version", version.string ()); if (priority) @@ -1750,7 +1704,8 @@ namespace bpkg auto bad_value = [&p, &s](const string& d) { throw serialization ( - s.name (), d + " for " + p.name + "-" + p.version.string ()); + s.name (), + d + " for " + p.name.string () + "-" + p.version.string ()); }; if (p.description && p.description->file) diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx index 97239db..82e580d 100644 --- a/libbpkg/manifest.hxx +++ b/libbpkg/manifest.hxx @@ -18,6 +18,8 @@ #include #include +#include + #include #include @@ -313,7 +315,7 @@ namespace bpkg struct dependency { - std::string name; + package_name name; butl::optional constraint; }; @@ -376,13 +378,6 @@ namespace bpkg comment (std::move (c)) {} }; - // Check if the package name complies with the specification (see the bpkg - // manual for details) and throw std::invalid_argument if that's not the - // case. - // - LIBBPKG_EXPORT void - validate_package_name (const std::string&); - class LIBBPKG_EXPORT package_manifest { public: @@ -391,7 +386,7 @@ namespace bpkg using url_type = bpkg::url; using email_type = bpkg::email; - std::string name; + package_name name; version_type version; butl::optional priority; std::string summary; diff --git a/libbpkg/package-name.cxx b/libbpkg/package-name.cxx new file mode 100644 index 0000000..c90d861 --- /dev/null +++ b/libbpkg/package-name.cxx @@ -0,0 +1,59 @@ +// file : libbpkg/package-name.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include +#include // move() +#include // find() +#include // invalid_argument + +#include // alpha(), alnum() + +using namespace std; +using namespace butl; + +namespace bpkg +{ + // package_name + // + static const vector illegal_pkg_names ({ + "build", + "con", "prn", "aux", "nul", + "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", + "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9"}); + + static const string legal_pkg_chars ("_+-."); + + package_name:: + package_name (std::string&& nm) + { + if (nm.size () < 2) + throw invalid_argument ("length is less than two characters"); + + if (find (illegal_pkg_names.begin (), illegal_pkg_names.end (), nm) != + illegal_pkg_names.end ()) + throw invalid_argument ("illegal name"); + + if (!alpha (nm.front ())) + throw invalid_argument ("illegal first character (must be alphabetic)"); + + // Here we rely on the fact that the name length >= 2. + // + for (auto i (nm.cbegin () + 1), e (nm.cend () - 1); i != e; ++i) + { + char c (*i); + + if (!(alnum(c) || legal_pkg_chars.find (c) != string::npos)) + throw invalid_argument ("illegal character"); + } + + if (!alnum (nm.back ())) + throw invalid_argument ( + "illegal last character (must be alphabetic or digit)"); + + value_ = move (nm); + } +} diff --git a/libbpkg/package-name.hxx b/libbpkg/package-name.hxx new file mode 100644 index 0000000..6c7ec6c --- /dev/null +++ b/libbpkg/package-name.hxx @@ -0,0 +1,194 @@ +// file : libbpkg/package-name.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBPKG_PACKAGE_NAME_HXX +#define LIBBPKG_PACKAGE_NAME_HXX + +#include +#include // move() +#include + +#include // casecmp() + +#include +#include + +namespace bpkg +{ + class LIBBPKG_EXPORT package_name + { + public: + // Create package name from string verifying that it complied with the + // specification and throwing std::invalid_argument if that's not the + // case. Note that in this case the passed value is guaranteed to be + // unchanged. + // + explicit + package_name (const std::string& s): package_name (std::string (s)) {} + + explicit + package_name (std::string&&); + + // Create a special empty package name. + // + package_name () = default; + + // Create an arbitrary string that can be used in contexts that expect + // a package name. For example, a package name pattern for use in ODB query + // expressions. + // + enum raw_string_type {raw_string}; + package_name (std::string s, raw_string_type): value_ (std::move (s)) {} + + bool + empty () const noexcept {return value_.empty ();} + + const std::string& + string () const& noexcept {return value_;} + + // Moves the underlying package name string out of the package name object. + // The object becomes empty. Usage: std::move (name).string (). + // + std::string + string () && {std::string r; r.swap (this->value_); return r;} + + // Compare ignoring case. Note that a string is not checked to be a valid + // package name. + // + int compare (const package_name& n) const {return compare (n.value_);} + int compare (const std::string& n) const {return compare (n.c_str ());} + int compare (const char* n) const {return butl::casecmp (value_, n);} + + private: + std::string value_; + }; + + inline bool + operator< (const package_name& x, const package_name& y) + { + return x.compare (y) < 0; + } + + inline bool + operator> (const package_name& x, const package_name& y) + { + return x.compare (y) > 0; + } + + inline bool + operator== (const package_name& x, const package_name& y) + { + return x.compare (y) == 0; + } + + inline bool + operator<= (const package_name& x, const package_name& y) + { + return x.compare (y) <= 0; + } + + inline bool + operator>= (const package_name& x, const package_name& y) + { + return x.compare (y) >= 0; + } + + inline bool + operator!= (const package_name& x, const package_name& y) + { + return x.compare (y) != 0; + } + + template + inline auto + operator< (const package_name& x, const T& y) + { + return x.compare (y) < 0; + } + + template + inline auto + operator> (const package_name& x, const T& y) + { + return x.compare (y) > 0; + } + + template + inline auto + operator== (const package_name& x, const T& y) + { + return x.compare (y) == 0; + } + + template + inline auto + operator<= (const package_name& x, const T& y) + { + return x.compare (y) <= 0; + } + + template + inline auto + operator>= (const package_name& x, const T& y) + { + return x.compare (y) >= 0; + } + + template + inline auto + operator!= (const package_name& x, const T& y) + { + return x.compare (y) != 0; + } + + template + inline auto + operator< (const T& x, const package_name& y) + { + return y > x; + } + + template + inline auto + operator> (const T& x, const package_name& y) + { + return y < x; + } + + template + inline auto + operator== (const T& x, const package_name& y) + { + return y == x; + } + + template + inline auto + operator<= (const T& x, const package_name& y) + { + return y >= x; + } + + template + inline auto + operator>= (const T& x, const package_name& y) + { + return y <= x; + } + + template + inline auto + operator!= (const T& x, const package_name& y) + { + return y != x; + } + + inline std::ostream& + operator<< (std::ostream& os, const package_name& v) + { + return os << v.string (); + } +} + +#endif // LIBBPKG_PACKAGE_NAME_HXX -- cgit v1.1