From 84f6ebab62a9f2553ae454d67861b0b142470d0b Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 25 Jul 2018 20:11:47 +0300 Subject: Move bpkg::package_name class to butl::project_name --- libbutl/project-name.cxx | 115 ++++++++++++++++++++ libbutl/project-name.mxx | 223 ++++++++++++++++++++++++++++++++++++++ tests/project-name/buildfile | 8 ++ tests/project-name/driver.cxx | 84 ++++++++++++++ tests/project-name/testscript | 78 +++++++++++++ tests/standard-version/buildfile | 2 +- tests/standard-version/driver.cxx | 10 +- 7 files changed, 514 insertions(+), 6 deletions(-) create mode 100644 libbutl/project-name.cxx create mode 100644 libbutl/project-name.mxx create mode 100644 tests/project-name/buildfile create mode 100644 tests/project-name/driver.cxx create mode 100644 tests/project-name/testscript diff --git a/libbutl/project-name.cxx b/libbutl/project-name.cxx new file mode 100644 index 0000000..5f94ea0 --- /dev/null +++ b/libbutl/project-name.cxx @@ -0,0 +1,115 @@ +// file : libbutl/project-name.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef __cpp_modules +#include +#endif + +#ifndef __cpp_lib_modules +#include +#include +#include // move() +#include // back_inserter +#include // find(), transform() +#include // invalid_argument +#endif + +// Other includes. + +#ifdef __cpp_modules +module butl.project_name; + +// Only imports additional to interface. +#ifdef __clang__ +#ifdef __cpp_lib_modules +import std.core; +import std.io; +#endif +import butl.utility; +#endif + +import butl.path; // path::traits +import butl.utility; // alpha(), alnum() +#else +#include +#include +#endif + +using namespace std; + +namespace butl +{ + // project_name + // + static const vector illegal_prj_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_prj_chars ("_+-."); + + project_name:: + project_name (std::string&& nm) + { + if (nm.size () < 2) + throw invalid_argument ("length is less than two characters"); + + if (find (illegal_prj_names.begin (), illegal_prj_names.end (), nm) != + illegal_prj_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_prj_chars.find (c) != string::npos)) + throw invalid_argument ("illegal character"); + } + + if (!alnum (nm.back ()) && nm.back () != '+') + throw invalid_argument ( + "illegal last character (must be alphabetic, digit, or plus)"); + + value_ = move (nm); + } + + string project_name:: + base () const + { + using std::string; + + size_t p (path::traits::find_extension (value_)); + return string (value_, 0, p); + } + + string project_name:: + extension () const + { + using std::string; + + size_t p (path::traits::find_extension (value_)); + return p != string::npos ? string (value_, p + 1) : string (); + } + + string project_name:: + variable () const + { + using std::string; + + auto sanitize = [] (char c) + { + return (c == '-' || c == '+' || c == '.') ? '_' : c; + }; + + string r; + transform (value_.begin (), value_.end (), back_inserter (r), sanitize); + return r; + } +} diff --git a/libbutl/project-name.mxx b/libbutl/project-name.mxx new file mode 100644 index 0000000..bb92340 --- /dev/null +++ b/libbutl/project-name.mxx @@ -0,0 +1,223 @@ +// file : libbutl/project-name.mxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef __cpp_modules +#pragma once +#endif + +// C includes. + +#ifndef __cpp_lib_modules +#include +#include // move() +#include +#endif + +// Other includes. + +#ifdef __cpp_modules +export module butl.project_name; +#ifdef __cpp_lib_modules +import std.core; +import std.io; +#endif +import butl.utility; // casecmp() +#else +#include +#endif + +#include + +LIBBUTL_MODEXPORT namespace butl +{ + class LIBBUTL_SYMEXPORT project_name + { + public: + // Create project 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 + project_name (const std::string& s): project_name (std::string (s)) {} + + explicit + project_name (std::string&&); + + // Create a special empty project name. + // + project_name () = default; + + // Create an arbitrary string that can be used in contexts that expect + // a project name. For example, a project name pattern for use in ODB query + // expressions. + // + enum raw_string_type {raw_string}; + project_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 project name string out of the project name object. + // The object becomes empty. Usage: std::move (name).string (). + // + std::string + string () && {std::string r; r.swap (this->value_); return r;} + + // Project name base and extension (without the dot). If there is no + // extension, then the base name is the same as the full name and the + // returned extension is empty. + // + std::string + base () const; + + std::string + extension () const; + + // Project name sanitized to a canonical variable name. Specifically, + // '.', '-', and '+' are replaced with '_'. + // + std::string + variable () const; + + // Compare ignoring case. Note that a string is not checked to be a valid + // project name. + // + int compare (const project_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 project_name& x, const project_name& y) + { + return x.compare (y) < 0; + } + + inline bool + operator> (const project_name& x, const project_name& y) + { + return x.compare (y) > 0; + } + + inline bool + operator== (const project_name& x, const project_name& y) + { + return x.compare (y) == 0; + } + + inline bool + operator<= (const project_name& x, const project_name& y) + { + return x.compare (y) <= 0; + } + + inline bool + operator>= (const project_name& x, const project_name& y) + { + return x.compare (y) >= 0; + } + + inline bool + operator!= (const project_name& x, const project_name& y) + { + return x.compare (y) != 0; + } + + template + inline auto + operator< (const project_name& x, const T& y) + { + return x.compare (y) < 0; + } + + template + inline auto + operator> (const project_name& x, const T& y) + { + return x.compare (y) > 0; + } + + template + inline auto + operator== (const project_name& x, const T& y) + { + return x.compare (y) == 0; + } + + template + inline auto + operator<= (const project_name& x, const T& y) + { + return x.compare (y) <= 0; + } + + template + inline auto + operator>= (const project_name& x, const T& y) + { + return x.compare (y) >= 0; + } + + template + inline auto + operator!= (const project_name& x, const T& y) + { + return x.compare (y) != 0; + } + + template + inline auto + operator< (const T& x, const project_name& y) + { + return y > x; + } + + template + inline auto + operator> (const T& x, const project_name& y) + { + return y < x; + } + + template + inline auto + operator== (const T& x, const project_name& y) + { + return y == x; + } + + template + inline auto + operator<= (const T& x, const project_name& y) + { + return y >= x; + } + + template + inline auto + operator>= (const T& x, const project_name& y) + { + return y <= x; + } + + template + inline auto + operator!= (const T& x, const project_name& y) + { + return y != x; + } + + inline std::ostream& + operator<< (std::ostream& os, const project_name& v) + { + return os << v.string (); + } +} diff --git a/tests/project-name/buildfile b/tests/project-name/buildfile new file mode 100644 index 0000000..3e16fe8 --- /dev/null +++ b/tests/project-name/buildfile @@ -0,0 +1,8 @@ +# file : tests/project-name/buildfile +# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +import libs = libbutl%lib{butl} +libs += $stdmod_lib + +exe{driver}: {hxx cxx}{*} $libs testscript diff --git a/tests/project-name/driver.cxx b/tests/project-name/driver.cxx new file mode 100644 index 0000000..51c8782 --- /dev/null +++ b/tests/project-name/driver.cxx @@ -0,0 +1,84 @@ +// file : tests/project-name/driver.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#ifndef __cpp_lib_modules +#include // ios::*bit +#include +#include +#include // invalid_argument +#endif + +// Other includes. + +#ifdef __cpp_modules +#ifdef __cpp_lib_modules +import std.core; +import std.io; +#endif +import butl.utility; // operator<<(ostream,exception), eof(), *case() +import butl.project_name; +#else +#include +#include +#endif + +using namespace std; +using namespace butl; + +// Create project_name from string and also perform some tests for the created +// object. +// +static project_name +name (const string& s) +{ + project_name r (s); + + assert (r == project_name (lcase (s))); + assert (r == project_name (ucase (s))); + + assert (r > project_name ("!", project_name::raw_string)); + assert (r < project_name ("~", project_name::raw_string)); + + return r; +} + +// Usage: argv[0] (string|base|extension|variable) +// +// Create project names from stdin lines, and for each of them print the +// result of the specified member function to stdout, one per line. +// +int +main (int argc, char* argv[]) +try +{ + assert (argc == 2); + + string m (argv[1]); + assert (m == "string" || m == "base" || m == "extension" || m == "variable"); + + cin.exceptions (ios::badbit); + cout.exceptions (ios::failbit | ios::badbit); + + string l; + while (!eof (getline (cin, l))) + { + project_name n (name (l)); + + const string& s (m == "string" ? n.string () : + m == "base" ? n.base () : + m == "extension" ? n.extension () : + n.variable ()); + + cout << s << endl; + } + + return 0; +} +catch (const invalid_argument& e) +{ + cerr << e << endl; + return 1; +} diff --git a/tests/project-name/testscript b/tests/project-name/testscript new file mode 100644 index 0000000..92bd5e8 --- /dev/null +++ b/tests/project-name/testscript @@ -0,0 +1,78 @@ +# file : tests/project-name/testscript +# copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +: string +{ + test.arguments += 'string' + + : valid + : + $* <>EOF + foo + foo1 + Foo + foo_bar + foo+bar + foo-bar + foo.bar + build2 + EOF + + $* <'a' 2>'length is less than two characters' != 0: short + $* <'nul' 2>'illegal name' != 0: illegal-name + $* <'1a' 2>'illegal first character (must be alphabetic)' != 0: illegal-first-char + $* <'a!b' 2>'illegal character' != 0: illegal-char + + : illegal-last-char + : + $* <'a.' 2>'illegal last character (must be alphabetic, digit, or plus)' != 0 +} + +: base +: +{ + test.arguments += 'base'; + + $* <>EOO + libbutl + libbutl.bash + a.b.c + EOI + libbutl + libbutl + a.b + EOO +} + +: extension +: +{ + test.arguments += 'extension'; + + $* <>EOO + libbutl + libbutl.bash + a.b.c + EOI + + bash + c + EOO +} + +: variable +: +{ + test.arguments += 'variable'; + + $* <>EOO + foo-bar + libc++ + libbutl.bash + EOI + foo_bar + libc__ + libbutl_bash + EOO +} diff --git a/tests/standard-version/buildfile b/tests/standard-version/buildfile index 8e8ded6..7ae054e 100644 --- a/tests/standard-version/buildfile +++ b/tests/standard-version/buildfile @@ -1,4 +1,4 @@ -# file : tests/tab-parser/buildfile +# file : tests/standard-version/buildfile # copyright : Copyright (c) 2014-2018 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file diff --git a/tests/standard-version/driver.cxx b/tests/standard-version/driver.cxx index 3bc1d31..7ad5b00 100644 --- a/tests/standard-version/driver.cxx +++ b/tests/standard-version/driver.cxx @@ -18,7 +18,7 @@ import std.core; import std.io; #endif -import butl.utility; // operator<<(ostream,exception) +import butl.utility; // operator<<(ostream,exception), eof() import butl.standard_version; #else #include @@ -124,11 +124,11 @@ version (const string& s, // -a output 'y' for alpha-version, 'n' otherwise // -b output 'y' for beta-version, 'n' otherwise // -c output 0 if versions are equal, -1 if the first one is less, 1 otherwise -// -r create version constraints from STDIN lines, and print them to STDOUT +// -r create version constraints from stdin lines, and print them to stdout // -s output 'y' if version satisfies constraint, 'n' otherwise // -// If no options are specified, then create versions from STDIN lines, and -// print them to STDOUT. +// If no options are specified, then create versions from stdin lines, and +// print them to stdout. // int main (int argc, char* argv[]) @@ -186,7 +186,7 @@ try } string s; - while (getline (cin, s)) + while (!eof (getline (cin, s))) cout << (s.empty () ? standard_version () : version (s)) << endl; return 0; -- cgit v1.1