From 8f3d3956b1e837c726859eb8bbe19dad79c54a42 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sat, 29 Apr 2017 23:55:46 +0300 Subject: Add hxx extension for headers and lib prefix for library dirs --- brep/.gitignore | 10 - brep/build | 166 ------------ brep/build.cxx | 48 ---- brep/build.xml | 67 ----- brep/buildfile | 50 ---- brep/common | 341 ------------------------ brep/common.cxx | 10 - brep/database-lock | 43 ---- brep/database-lock.cxx | 44 ---- brep/odb.sh | 37 --- brep/package | 500 ------------------------------------ brep/package-extra.sql | 130 ---------- brep/package-traits | 38 --- brep/package-traits.cxx | 70 ----- brep/package.cxx | 169 ------------ brep/package.xml | 417 ------------------------------ brep/types | 94 ------- brep/utility | 34 --- brep/version.in | 74 ------ brep/wrapper-traits | 63 ----- build/export.build | 4 +- build/root.build | 2 +- buildfile | 2 +- libbrep/.gitignore | 10 + libbrep/build.cxx | 48 ++++ libbrep/build.hxx | 166 ++++++++++++ libbrep/build.xml | 67 +++++ libbrep/buildfile | 50 ++++ libbrep/common.cxx | 10 + libbrep/common.hxx | 341 ++++++++++++++++++++++++ libbrep/database-lock.cxx | 44 ++++ libbrep/database-lock.hxx | 43 ++++ libbrep/odb.sh | 40 +++ libbrep/package-extra.sql | 130 ++++++++++ libbrep/package-traits.cxx | 70 +++++ libbrep/package-traits.hxx | 38 +++ libbrep/package.cxx | 169 ++++++++++++ libbrep/package.hxx | 500 ++++++++++++++++++++++++++++++++++++ libbrep/package.xml | 417 ++++++++++++++++++++++++++++++ libbrep/types.hxx | 94 +++++++ libbrep/utility.hxx | 34 +++ libbrep/version.hxx.in | 74 ++++++ libbrep/wrapper-traits.hxx | 63 +++++ load/buildfile | 8 +- load/load.cli | 2 +- load/load.cxx | 12 +- load/types-parsers | 28 -- load/types-parsers.cxx | 4 +- load/types-parsers.hxx | 28 ++ migrate/buildfile | 10 +- migrate/migrate.cxx | 10 +- mod/build-config | 23 -- mod/build-config.cxx | 2 +- mod/build-config.hxx | 23 ++ mod/buildfile | 10 +- mod/database | 27 -- mod/database-module | 73 ------ mod/database-module.cxx | 8 +- mod/database-module.hxx | 73 ++++++ mod/database.cxx | 2 +- mod/database.hxx | 27 ++ mod/diagnostics | 306 ---------------------- mod/diagnostics.cxx | 2 +- mod/diagnostics.hxx | 306 ++++++++++++++++++++++ mod/mod-build-force | 45 ---- mod/mod-build-force.cxx | 14 +- mod/mod-build-force.hxx | 45 ++++ mod/mod-build-log | 45 ---- mod/mod-build-log.cxx | 14 +- mod/mod-build-log.hxx | 45 ++++ mod/mod-build-result | 42 --- mod/mod-build-result.cxx | 18 +- mod/mod-build-result.hxx | 42 +++ mod/mod-build-task | 42 --- mod/mod-build-task.cxx | 18 +- mod/mod-build-task.hxx | 42 +++ mod/mod-package-details | 42 --- mod/mod-package-details.cxx | 16 +- mod/mod-package-details.hxx | 42 +++ mod/mod-package-search | 42 --- mod/mod-package-search.cxx | 16 +- mod/mod-package-search.hxx | 42 +++ mod/mod-package-version-details | 45 ---- mod/mod-package-version-details.cxx | 16 +- mod/mod-package-version-details.hxx | 45 ++++ mod/mod-repository-details | 42 --- mod/mod-repository-details.cxx | 16 +- mod/mod-repository-details.hxx | 42 +++ mod/mod-repository-root | 77 ------ mod/mod-repository-root.cxx | 26 +- mod/mod-repository-root.hxx | 77 ++++++ mod/module | 201 --------------- mod/module.cxx | 8 +- mod/module.hxx | 201 +++++++++++++++ mod/options-types | 31 --- mod/options-types.hxx | 31 +++ mod/options.cli | 6 +- mod/page | 428 ------------------------------ mod/page.cxx | 10 +- mod/page.hxx | 428 ++++++++++++++++++++++++++++++ mod/services.cxx | 8 +- mod/types-parsers | 64 ----- mod/types-parsers.cxx | 4 +- mod/types-parsers.hxx | 64 +++++ tests/load/buildfile | 4 +- tests/load/driver.cxx | 8 +- tests/web/xhtml/driver.cxx | 2 +- web/apache/log | 81 ------ web/apache/log.hxx | 81 ++++++ web/apache/request | 190 -------------- web/apache/request.cxx | 4 +- web/apache/request.hxx | 190 ++++++++++++++ web/apache/service | 330 ------------------------ web/apache/service.cxx | 6 +- web/apache/service.hxx | 330 ++++++++++++++++++++++++ web/apache/stream | 149 ----------- web/apache/stream.hxx | 149 +++++++++++ web/mime-url-encoding | 30 --- web/mime-url-encoding.cxx | 2 +- web/mime-url-encoding.hxx | 30 +++ web/module | 267 ------------------- web/module.hxx | 267 +++++++++++++++++++ web/xhtml | 351 ------------------------- web/xhtml-fragment | 48 ---- web/xhtml-fragment.cxx | 4 +- web/xhtml-fragment.hxx | 48 ++++ web/xhtml.hxx | 351 +++++++++++++++++++++++++ 127 files changed, 5606 insertions(+), 5603 deletions(-) delete mode 100644 brep/.gitignore delete mode 100644 brep/build delete mode 100644 brep/build.cxx delete mode 100644 brep/build.xml delete mode 100644 brep/buildfile delete mode 100644 brep/common delete mode 100644 brep/common.cxx delete mode 100644 brep/database-lock delete mode 100644 brep/database-lock.cxx delete mode 100755 brep/odb.sh delete mode 100644 brep/package delete mode 100644 brep/package-extra.sql delete mode 100644 brep/package-traits delete mode 100644 brep/package-traits.cxx delete mode 100644 brep/package.cxx delete mode 100644 brep/package.xml delete mode 100644 brep/types delete mode 100644 brep/utility delete mode 100644 brep/version.in delete mode 100644 brep/wrapper-traits create mode 100644 libbrep/.gitignore create mode 100644 libbrep/build.cxx create mode 100644 libbrep/build.hxx create mode 100644 libbrep/build.xml create mode 100644 libbrep/buildfile create mode 100644 libbrep/common.cxx create mode 100644 libbrep/common.hxx create mode 100644 libbrep/database-lock.cxx create mode 100644 libbrep/database-lock.hxx create mode 100755 libbrep/odb.sh create mode 100644 libbrep/package-extra.sql create mode 100644 libbrep/package-traits.cxx create mode 100644 libbrep/package-traits.hxx create mode 100644 libbrep/package.cxx create mode 100644 libbrep/package.hxx create mode 100644 libbrep/package.xml create mode 100644 libbrep/types.hxx create mode 100644 libbrep/utility.hxx create mode 100644 libbrep/version.hxx.in create mode 100644 libbrep/wrapper-traits.hxx delete mode 100644 load/types-parsers create mode 100644 load/types-parsers.hxx delete mode 100644 mod/build-config create mode 100644 mod/build-config.hxx delete mode 100644 mod/database delete mode 100644 mod/database-module create mode 100644 mod/database-module.hxx create mode 100644 mod/database.hxx delete mode 100644 mod/diagnostics create mode 100644 mod/diagnostics.hxx delete mode 100644 mod/mod-build-force create mode 100644 mod/mod-build-force.hxx delete mode 100644 mod/mod-build-log create mode 100644 mod/mod-build-log.hxx delete mode 100644 mod/mod-build-result create mode 100644 mod/mod-build-result.hxx delete mode 100644 mod/mod-build-task create mode 100644 mod/mod-build-task.hxx delete mode 100644 mod/mod-package-details create mode 100644 mod/mod-package-details.hxx delete mode 100644 mod/mod-package-search create mode 100644 mod/mod-package-search.hxx delete mode 100644 mod/mod-package-version-details create mode 100644 mod/mod-package-version-details.hxx delete mode 100644 mod/mod-repository-details create mode 100644 mod/mod-repository-details.hxx delete mode 100644 mod/mod-repository-root create mode 100644 mod/mod-repository-root.hxx delete mode 100644 mod/module create mode 100644 mod/module.hxx delete mode 100644 mod/options-types create mode 100644 mod/options-types.hxx delete mode 100644 mod/page create mode 100644 mod/page.hxx delete mode 100644 mod/types-parsers create mode 100644 mod/types-parsers.hxx delete mode 100644 web/apache/log create mode 100644 web/apache/log.hxx delete mode 100644 web/apache/request create mode 100644 web/apache/request.hxx delete mode 100644 web/apache/service create mode 100644 web/apache/service.hxx delete mode 100644 web/apache/stream create mode 100644 web/apache/stream.hxx delete mode 100644 web/mime-url-encoding create mode 100644 web/mime-url-encoding.hxx delete mode 100644 web/module create mode 100644 web/module.hxx delete mode 100644 web/xhtml delete mode 100644 web/xhtml-fragment create mode 100644 web/xhtml-fragment.hxx create mode 100644 web/xhtml.hxx diff --git a/brep/.gitignore b/brep/.gitignore deleted file mode 100644 index 1e5e1f9..0000000 --- a/brep/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -common-odb* - -package-odb* -package.sql -package-extra - -build-odb* -build.sql - -version diff --git a/brep/build b/brep/build deleted file mode 100644 index 43ca230..0000000 --- a/brep/build +++ /dev/null @@ -1,166 +0,0 @@ -// file : brep/build -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BREP_BUILD -#define BREP_BUILD - -#include - -#include -#include - -#include - -#include -#include - -#include // Must be included last (see assert). - -// Used by the data migration entries. -// -#define LIBBREP_BUILD_SCHEMA_VERSION_BASE 1 - -#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 1, open) - -// We have to keep these mappings at the global scope instead of inside -// the brep namespace because they need to be also effective in the -// bbot namespace from which we "borrow" types (and some of them use the mapped -// types). -// -#pragma db map type(bbot::result_status) as(std::string) \ - to(to_string (?)) \ - from(bbot::to_result_status (?)) - -namespace brep -{ - #pragma db value - struct build_id - { - package_id package; - string configuration; - - build_id () = default; - build_id (package_id p, string c) - : package (move (p)), configuration (move (c)) {} - }; - - inline bool - operator< (const build_id& x, const build_id& y) - { - return - x.package < y.package ? true : - y.package < x.package ? false : - x.configuration < y.configuration; - } - - // build_state - // - enum class build_state: std::uint8_t - { - untested, - testing, - tested - }; - - string - to_string (build_state); - - build_state - to_build_state (const string&); // May throw invalid_argument. - - inline ostream& - operator<< (ostream& os, build_state s) {return os << to_string (s);} - - #pragma db map type(build_state) as(string) \ - to(to_string (?)) \ - from(brep::to_build_state (?)) - - // result_status - // - using bbot::result_status; - - using optional_result_status = optional; - - #pragma db map type(optional_result_status) as(optional_string) \ - to((?) ? bbot::to_string (*(?)) : brep::optional_string ()) \ - from((?) \ - ? bbot::to_result_status (*(?)) \ - : brep::optional_result_status ()) - - // operation_results - // - using bbot::operation_result; - #pragma db value(operation_result) definition - - using bbot::operation_results; - - #pragma db object pointer(shared_ptr) session - class build - { - public: - using timestamp_type = brep::timestamp; - - // Create the build object with the testing state, non-existent status, - // the timestamp set to now and the forced flag set to false. - // - build (string name, version, - string configuration, - string machine, string machine_summary); - - build_id id; - - string& package_name; // Tracks id.package.name. - upstream_version package_version; // Original of id.package.version. - string& configuration; // Tracks id.configuration. - - build_state state; - - // Time of the last state change (the creation time initially). - // - timestamp_type timestamp; - - // True if the package rebuild has been forced. - // - bool forced; - - // Present only if the state is 'tested'. - // - optional status; - - // Present only if the state is 'testing' or 'tested'. - // - optional machine; - optional machine_summary; - - // Note that the logs are stored as std::string/TEXT which is Ok since - // they are UTF-8 and our database is UTF-8. - // - #pragma db section(results_section) - operation_results results; - - #pragma db load(lazy) update(always) - odb::section results_section; - - // Database mapping. - // - #pragma db member(id) id column("") - - #pragma db member(package_name) transient - #pragma db member(package_version) \ - set(this.package_version.init (this.id.package.version, (?))) - #pragma db member(configuration) transient - - #pragma db member(results) id_column("") value_column("") - - build (const build&) = delete; - build& operator= (const build&) = delete; - - private: - friend class odb::access; - build () - : package_name (id.package.name), configuration (id.configuration) {} - }; -} - -#endif // BREP_BUILD diff --git a/brep/build.cxx b/brep/build.cxx deleted file mode 100644 index d795ba6..0000000 --- a/brep/build.cxx +++ /dev/null @@ -1,48 +0,0 @@ -// file : brep/build.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -namespace brep -{ - // build_state - // - string - to_string (build_state s) - { - switch (s) - { - case build_state::untested: return "untested"; - case build_state::testing: return "testing"; - case build_state::tested: return "tested"; - } - - return string (); // Should never reach. - } - - build_state - to_build_state (const string& s) - { - if (s == "untested") return build_state::untested; - else if (s == "testing") return build_state::testing; - else if (s == "tested") return build_state::tested; - else throw invalid_argument ("invalid build state '" + s + "'"); - } - - // build - // - build:: - build (string pnm, version pvr, string cfg, string mnm, string msm) - : id (package_id (move (pnm), pvr), move (cfg)), - package_name (id.package.name), - package_version (move (pvr)), - configuration (id.configuration), - state (build_state::testing), - timestamp (timestamp_type::clock::now ()), - forced (false), - machine (move (mnm)), - machine_summary (move (msm)) - { - } -} diff --git a/brep/build.xml b/brep/build.xml deleted file mode 100644 index 8d9178a..0000000 --- a/brep/build.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
diff --git a/brep/buildfile b/brep/buildfile deleted file mode 100644 index 2da0281..0000000 --- a/brep/buildfile +++ /dev/null @@ -1,50 +0,0 @@ -# file : brep/buildfile -# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file - -define sql: file -sql{*}: extension = sql -sql{*}: install = data/ - -import int_libs = libodb%lib{odb} -import int_libs += libodb-pgsql%lib{odb-pgsql} -import int_libs += libbutl%lib{butl} -import int_libs += libbpkg%lib{bpkg} -import int_libs += libbbot%lib{bbot} - -lib{brep}: \ -{hxx cxx}{ build } \ -{file }{ build.xml } \ -{hxx ixx cxx}{ build-odb } \ -{hxx cxx}{ common } \ -{hxx ixx cxx}{ common-odb } \ -{hxx cxx}{ package } \ -{file }{ package.xml } \ -{hxx ixx cxx}{ package-odb } \ -{hxx }{ package-extra } \ -{hxx cxx}{ package-traits } \ -{hxx cxx}{ database-lock } \ -{hxx }{ types } \ -{hxx }{ utility } \ -{hxx }{ version } \ -{hxx }{ wrapper-traits } \ - $int_libs \ -sql{build package package-extra} - -hxx{version}: in{version} $src_root/file{manifest} -hxx{version}: dist = true - -# For pre-releases use the complete version to make sure they cannot be used -# in place of another pre-release or the final version. -# -if $version.pre_release - lib{brep}: bin.lib.version = @"-$version.project_id" -else - lib{brep}: bin.lib.version = @"-$version.major.$version.minor" - -lib{brep}: cxx.export.poptions = "-I$out_root" "-I$src_root" -lib{brep}: cxx.export.libs = $int_libs - -# Install into the brep/ subdirectory of, say, /usr/include/. -# -install.include = $install.include/brep/ diff --git a/brep/common b/brep/common deleted file mode 100644 index 52f225c..0000000 --- a/brep/common +++ /dev/null @@ -1,341 +0,0 @@ -// file : brep/common -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BREP_COMMON -#define BREP_COMMON - -#include -#include -#include // static_assert - -#include -#include - -// The uint16_t value range is not fully covered by SMALLINT PostgreSQL type -// to which uint16_t is mapped by default. -// -#pragma db value(uint16_t) type("INTEGER") - -namespace brep -{ - // Use an image type to map bpkg::version to the database since there - // is no way to modify individual components directly. - // - #pragma db value - struct _version - { - uint16_t epoch; - string canonical_upstream; - string canonical_release; - uint16_t revision; - string upstream; - optional release; - }; -} - -#include - -namespace brep -{ - using optional_version = optional; - using _optional_version = optional<_version>; -} - -// Prevent assert() macro expansion in get/set expressions. This should -// appear after all #include directives since the assert() macro is -// redefined in each inclusion. -// -#ifdef ODB_COMPILER -# undef assert -# define assert assert -void assert (int); -#endif - -// We have to keep these mappings at the global scope instead of inside -// the brep namespace because they need to be also effective in the -// bpkg namespace from which we "borrow" types (and some of them use version). -// -#pragma db map type(bpkg::version) as(brep::_version) \ - to(brep::_version{(?).epoch, \ - (?).canonical_upstream, \ - (?).canonical_release, \ - (?).revision, \ - (?).upstream, \ - (?).release}) \ - from(bpkg::version ((?).epoch, \ - std::move ((?).upstream), \ - std::move ((?).release), \ - (?).revision)) - -#pragma db map type(brep::optional_version) as(brep::_optional_version) \ - to((?) \ - ? brep::_version{(?)->epoch, \ - (?)->canonical_upstream, \ - (?)->canonical_release, \ - (?)->revision, \ - (?)->upstream, \ - (?)->release} \ - : brep::_optional_version ()) \ - from((?) \ - ? bpkg::version ((?)->epoch, \ - std::move ((?)->upstream), \ - std::move ((?)->release), \ - (?)->revision) \ - : brep::optional_version ()) - -namespace brep -{ - // path - // - #pragma db map type(path) as(string) to((?).string ()) from(brep::path (?)) - - using optional_path = optional; - using optional_string = optional; - - #pragma db map type(optional_path) as(brep::optional_string) \ - to((?) ? (?)->string () : brep::optional_string ()) \ - from((?) ? brep::path (*(?)) : brep::optional_path ()) - - #pragma db map type(dir_path) as(string) \ - to((?).string ()) from(brep::dir_path (?)) - - // Ensure that timestamp can be represented in nonoseconds without loss of - // accuracy, so the following ODB mapping is adequate. - // - static_assert( - std::ratio_greater_equal::value, - "The following timestamp ODB mapping is invalid"); - - // As it pointed out in butl/timestamp we will overflow in year 2262, but - // by that time some larger basic type will be available for mapping. - // - #pragma db map type(timestamp) as(uint64_t) \ - to(std::chrono::duration_cast ( \ - (?).time_since_epoch ()).count ()) \ - from(brep::timestamp ( \ - std::chrono::duration_cast ( \ - std::chrono::nanoseconds (?)))) - - // version - // - using bpkg::version; - - #pragma db value - struct canonical_version - { - uint16_t epoch; - string canonical_upstream; - string canonical_release; - uint16_t revision; - - bool - empty () const noexcept - { - // Note that an empty canonical_upstream doesn't denote an empty - // canonical_version. Remeber, that canonical_upstream doesn't include - // rightmost digit-only zero components? So non-empty version("0") has - // an empty canonical_upstream. - // - return epoch == 0 && canonical_upstream.empty () && - canonical_release.empty () && revision == 0; - } - - // Change collation to ensure the proper comparison of the "absent" release - // with a specified one. - // - // The default collation for UTF8-encoded TEXT columns in PostgreSQL is - // UCA-compliant. This makes the statement 'a' < '~' to be false, which - // in turn makes the statement 2.1.alpha < 2.1 to be false as well. - // - // Unicode Collation Algorithm (UCA): http://unicode.org/reports/tr10/ - // - #pragma db member(canonical_release) options("COLLATE \"C\"") - }; - - #pragma db value transient - struct upstream_version: version - { - #pragma db member(upstream_) virtual(string) \ - get(this.upstream) \ - set(this = brep::version (0, std::move (?), std::string (), 0)) - - #pragma db member(release_) virtual(optional_string) \ - get(this.release) \ - set(this = brep::version ( \ - 0, std::move (this.upstream), std::move (?), 0)) - - upstream_version () = default; - upstream_version (version v): version (move (v)) {} - upstream_version& - operator= (version v) {version& b (*this); b = v; return *this;} - - void - init (const canonical_version& cv, const upstream_version& uv) - { - *this = version (cv.epoch, uv.upstream, uv.release, cv.revision); - assert (cv.canonical_upstream == canonical_upstream && - cv.canonical_release == canonical_release); - } - }; - - // Wildcard version. Satisfies any dependency constraint and is represented - // as 0+0 (which is also the "stub version"; since a real version is always - // greater than the stub version, we reuse it to signify a special case). - // - extern const version wildcard_version; - - #pragma db value - struct package_id - { - string name; - canonical_version version; - - package_id () = default; - package_id (string n, const brep::version& v) - : name (move (n)), - version { - v.epoch, v.canonical_upstream, v.canonical_release, v.revision} - { - } - }; - - // Version comparison operators. - // - // They allow comparing objects that have epoch, canonical_upstream, - // canonical_release, and revision data members. The idea is that this - // works for both query members of types version and canonical_version - // as well as for comparing canonical_version to version. - // - template - inline auto - compare_version_eq (const T1& x, const T2& y, bool revision) - -> decltype (x.epoch == y.epoch) - { - // Since we don't quite know what T1 and T2 are (and where the resulting - // expression will run), let's not push our luck with something like - // (!revision || x.revision == y.revision). - // - auto r (x.epoch == y.epoch && - x.canonical_upstream == y.canonical_upstream && - x.canonical_release == y.canonical_release); - - return revision - ? r && x.revision == y.revision - : r; - } - - template - inline auto - compare_version_ne (const T1& x, const T2& y, bool revision) - -> decltype (x.epoch == y.epoch) - { - auto r (x.epoch != y.epoch || - x.canonical_upstream != y.canonical_upstream || - x.canonical_release != y.canonical_release); - - return revision - ? r || x.revision != y.revision - : r; - } - - template - inline auto - compare_version_lt (const T1& x, const T2& y, bool revision) - -> decltype (x.epoch == y.epoch) - { - auto r ( - x.epoch < y.epoch || - (x.epoch == y.epoch && x.canonical_upstream < y.canonical_upstream) || - (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && - x.canonical_release < y.canonical_release)); - - return revision - ? r || - (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && - x.canonical_release == y.canonical_release && x.revision < y.revision) - : r; - } - - template - inline auto - compare_version_le (const T1& x, const T2& y, bool revision) - -> decltype (x.epoch == y.epoch) - { - auto r ( - x.epoch < y.epoch || - (x.epoch == y.epoch && x.canonical_upstream < y.canonical_upstream)); - - return revision - ? r || - (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && - x.canonical_release < y.canonical_release) || - (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && - x.canonical_release == y.canonical_release && x.revision <= y.revision) - : r || - (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && - x.canonical_release <= y.canonical_release); - } - - template - inline auto - compare_version_gt (const T1& x, const T2& y, bool revision) - -> decltype (x.epoch == y.epoch) - { - auto r ( - x.epoch > y.epoch || - (x.epoch == y.epoch && x.canonical_upstream > y.canonical_upstream) || - (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && - x.canonical_release > y.canonical_release)); - - return revision - ? r || - (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && - x.canonical_release == y.canonical_release && x.revision > y.revision) - : r; - } - - template - inline auto - compare_version_ge (const T1& x, const T2& y, bool revision) - -> decltype (x.epoch == y.epoch) - { - auto r ( - x.epoch > y.epoch || - (x.epoch == y.epoch && x.canonical_upstream > y.canonical_upstream)); - - return revision - ? r || - (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && - x.canonical_release > y.canonical_release) || - (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && - x.canonical_release == y.canonical_release && x.revision >= y.revision) - : r || - (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && - x.canonical_release >= y.canonical_release); - } - - template - inline auto - order_by_version_desc (const T& x) -> //decltype ("ORDER BY" + x.epoch) - decltype (x.epoch == 0) - { - return "ORDER BY" - + x.epoch + "DESC," - + x.canonical_upstream + "DESC," - + x.canonical_release + "DESC," - + x.revision + "DESC"; - } - - inline bool - operator< (const package_id& x, const package_id& y) - { - if (int r = x.name.compare (y.name)) - return r < 0; - - return compare_version_lt (x.version, y.version, true); - } -} - -#endif // BREP_COMMON diff --git a/brep/common.cxx b/brep/common.cxx deleted file mode 100644 index 4847977..0000000 --- a/brep/common.cxx +++ /dev/null @@ -1,10 +0,0 @@ -// file : brep/common.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -namespace brep -{ - const version wildcard_version (0, "0", nullopt, 0); -} diff --git a/brep/database-lock b/brep/database-lock deleted file mode 100644 index 6bf21c9..0000000 --- a/brep/database-lock +++ /dev/null @@ -1,43 +0,0 @@ -// file : brep/database-lock -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BREP_DATABASE_LOCK -#define BREP_DATABASE_LOCK - -#include // database, transaction -#include - -#include -#include - -namespace brep -{ - struct database_locked: std::exception - { - virtual char const* - what () const throw () {return "database locked";} - }; - - // Try to "lock" the PostgreSQL database in the constructor and release the - // lock in the destructor. Throw database_locked if the database is already - // locked by someone else. May also throw odb::pgsql::database_exception. - // - // This mechanism is used by the brep loader and schema migration tool to - // make sure they don't step on each others toes. - // - // Note: movable but not copyable. - // - class database_lock - { - public: - explicit - database_lock (odb::pgsql::database&); - - private: - odb::pgsql::connection_ptr connection_; - unique_ptr transaction_; - }; -} - -#endif // BREP_DATABASE_LOCK diff --git a/brep/database-lock.cxx b/brep/database-lock.cxx deleted file mode 100644 index 88f3603..0000000 --- a/brep/database-lock.cxx +++ /dev/null @@ -1,44 +0,0 @@ -// file : brep/database-lock.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include -#include -#include - -namespace brep -{ - using namespace odb::pgsql; - - database_lock:: - database_lock (database& db) - { - // Before locking the table make sure it exists. - // - { - transaction t (db.begin ()); - db.execute ("CREATE TABLE IF NOT EXISTS database_mutex ()"); - t.commit (); - } - - connection_ = db.connection (); - - // Don't make current. Will be rolled back in destructor. - // - transaction_.reset (new transaction (connection_->begin (), false)); - - try - { - connection_->execute ("LOCK TABLE database_mutex NOWAIT"); - } - catch (const database_exception& e) - { - if (e.sqlstate () == "55P03") // The table is already locked. - throw database_locked (); - - throw; - } - } -} diff --git a/brep/odb.sh b/brep/odb.sh deleted file mode 100755 index 7fb7fa0..0000000 --- a/brep/odb.sh +++ /dev/null @@ -1,37 +0,0 @@ -#! /usr/bin/env bash - -trap 'exit 1' ERR - -odb=odb -lib="\ --I$HOME/work/odb/libodb-sqlite-default \ --I$HOME/work/odb/libodb-sqlite \ --I$HOME/work/odb/libodb-default \ --I$HOME/work/odb/libodb" - -$odb $lib -d pgsql --std c++11 --generate-query \ - --odb-epilogue '#include ' \ - --hxx-prologue '#include ' \ - -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \ - --hxx-suffix "" --include-with-brackets \ - --include-prefix brep --guard-prefix BREP \ - common - -$odb $lib -d pgsql --std c++11 --generate-query --generate-schema \ - --schema-format sql --schema-format embedded --schema-name package \ - --generate-prepared --odb-epilogue '#include ' \ - --hxx-prologue '#include ' \ - -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \ - --hxx-suffix "" --include-with-brackets \ - --include-prefix brep --guard-prefix BREP \ - package - -xxd -i package-extra - -$odb $lib -d pgsql --std c++11 --generate-query --generate-schema \ - --schema-format sql --schema-format embedded --schema-name build \ - --generate-prepared --odb-epilogue '#include ' \ - -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \ - --hxx-suffix "" --include-with-brackets \ - --include-prefix brep --guard-prefix BREP \ - build diff --git a/brep/package b/brep/package deleted file mode 100644 index ba7490f..0000000 --- a/brep/package +++ /dev/null @@ -1,500 +0,0 @@ -// file : brep/package -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BREP_PACKAGE -#define BREP_PACKAGE - -#include -#include - -#include -#include - -#include -#include - -#include // Must be included last (see assert). - -// Used by the data migration entries. -// -#define LIBBREP_PACKAGE_SCHEMA_VERSION_BASE 4 - -#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 4, open) - -namespace brep -{ - // @@ Might make sense to put some heavy members (e.g., description, - // containers) into a separate section. - // - // @@ Not sure there is a benefit in making tags a full-blown container - // (i.e., a separate table). Maybe provide a mapping of vector - // to TEXT as a comma-separated list. - // - - // Forward declarations. - // - class repository; - class package; - - // priority - // - using bpkg::priority; - - #pragma db value(priority) definition - #pragma db member(priority::value) column("") - - // url - // - using bpkg::url; - - #pragma db value(url) definition - #pragma db member(url::value) virtual(string) before access(this) column("") - - // email - // - using bpkg::email; - - #pragma db value(email) definition - #pragma db member(email::value) virtual(string) before access(this) column("") - - // licenses - // - using bpkg::licenses; - using license_alternatives = vector; - - #pragma db value(licenses) definition - - // dependencies - // - using bpkg::dependency_constraint; - - #pragma db value(dependency_constraint) definition - - // Notes: - // - // 1. Will the package be always resolvable? What if it is in - // another repository (i.e., a "chained" third-party repo). - // The question is then whether we will load such "third- - // party packages" (i.e., packages that are not in our - // repository). If the answer is yes, then we can have - // a pointer here. If the answer is no, then we can't. - // Also, if the answer is yes, we probably don't need to - // load as much information as for "our own" packages. We - // also shouldn't be showing them in search results, etc. - // I think all we need is to know which repository this - // package comes from so that we can tell the user. How are - // we going to capture this? Poly hierarchy of packages? - // - // 2. I believe we don't need to use a weak pointer here since - // there should be no package dependency cycles (and therefore - // ownership cycles). - // - // 3. Actually there can be dependency cycle as dependency referes not to - // just a package but a specific version, so for the same pair of - // packages dependency for different versions can have an opposite - // directions. The possible solution is instead of a package we point - // to the earliest version that satisfies the constraint. But this - // approach requires to ensure no cycles exist before instantiating - // package objects which in presense of "foreign" packages can be - // tricky. Can stick to just a package name until get some clarity on - // "foreign" package resolution. - // - // 4. As we left just the package class the dependency resolution come to - // finding the best version matching package object. The question is - // if to resolve dependencies on the loading phase or in the WEB interface - // when required. The arguments in favour of doing that during loading - // phase are: - // - // - WEB interface get offloaded from a possibly expensive queries - // which otherwise have to be executed multiple times for the same - // dependency no matter the result would be the same. - // - // - No need to complicate persisted object model with repository - // relations otherwise required just for dependency resolution. - // - - #pragma db value - struct dependency - { - using package_type = brep::package; - - lazy_shared_ptr package; - optional constraint; - - // Prerequisite package name. - // - string - name () const; - - // Database mapping. - // - #pragma db member(package) column("") not_null - #pragma db member(constraint) column("") - }; - - ostream& - operator<< (ostream&, const dependency&); - - bool - operator== (const dependency&, const dependency&); - - bool - operator!= (const dependency&, const dependency&); - - #pragma db value - class dependency_alternatives: public vector - { - public: - bool conditional; - bool buildtime; - string comment; - - dependency_alternatives () = default; - dependency_alternatives (bool d, bool b, string c) - : conditional (d), buildtime (b), comment (move (c)) {} - }; - - using dependencies = vector; - - // requirements - // - using bpkg::requirement_alternatives; - using requirements = vector; - - #pragma db value(requirement_alternatives) definition - - // repository_location - // - using bpkg::repository_location; - - #pragma db map type(repository_location) as(string) \ - to((?).string ()) from(brep::repository_location (?)) - - #pragma db value - class certificate - { - public: - string fingerprint; // SHA256 fingerprint. - string name; // CN component of Subject. - string organization; // O component of Subject. - string email; // email: in Subject Alternative Name. - string pem; // PEM representation. - }; - - #pragma db object pointer(shared_ptr) session - class repository - { - public: - using email_type = brep::email; - using certificate_type = brep::certificate; - - // Create internal repository. - // - repository (repository_location, - string display_name, - repository_location cache_location, - optional, - uint16_t priority); - - // Create external repository. - // - explicit - repository (repository_location); - - string name; // Object id (canonical name). - repository_location location; - string display_name; - - // The order in the internal repositories configuration file, starting from - // 1. 0 for external repositories. - // - uint16_t priority; - - optional url; - - // Present only for internal repositories. - // - optional email; - optional summary; - optional description; - - // Location of the repository local cache. Non empty for internal - // repositories and external ones with a filesystem path location. - // - repository_location cache_location; - - // Present only for internal signed repositories. - // - optional certificate; - - // Initialized with timestamp_nonexistent by default. - // - timestamp packages_timestamp; - - // Initialized with timestamp_nonexistent by default. - // - timestamp repositories_timestamp; - - bool internal; - vector> complements; - vector> prerequisites; - - // Database mapping. - // - #pragma db member(name) id - - #pragma db member(location) \ - set(this.location = std::move (?); \ - assert (this.name == this.location.canonical_name ())) - - #pragma db member(complements) id_column("repository") \ - value_column("complement") value_not_null - - #pragma db member(prerequisites) id_column("repository") \ - value_column("prerequisite") value_not_null - - private: - friend class odb::access; - repository () = default; - }; - - // The 'to' expression calls the PostgreSQL to_tsvector(weighted_text) - // function overload (package-extra.sql). Since we are only interested - // in "write-only" members of this type, make the 'from' expression - // always return empty string (we still have to work the placeholder - // in to keep overprotective ODB happy). - // - #pragma db map type("tsvector") as("TEXT") \ - to("to_tsvector((?)::weighted_text)") from("COALESCE('',(?))") - - // C++ type for weighted PostgreSQL tsvector. - // - #pragma db value type("tsvector") - struct weighted_text - { - string a; - string b; - string c; - string d; - }; - - #pragma db object pointer(shared_ptr) session - class package - { - public: - using repository_type = brep::repository; - using version_type = brep::version; - using priority_type = brep::priority; - using license_alternatives_type = brep::license_alternatives; - using url_type = brep::url; - using email_type = brep::email; - using dependencies_type = brep::dependencies; - using requirements_type = brep::requirements; - - // Create internal package object. - // - package (string name, - version_type, - priority_type, - string summary, - license_alternatives_type, - strings tags, - optional description, - string changes, - url_type, - optional package_url, - email_type, - optional package_email, - optional build_email, - dependencies_type, - requirements_type, - optional location, - optional sha256sum, - shared_ptr); - - // Create external package object. - // - // External repository packages can appear on the WEB interface only in - // dependency list in the form of a link to the corresponding WEB page. - // The only package information required to compose such a link is the - // package name, version, and repository location. - // - package (string name, version_type, shared_ptr); - - bool - internal () const noexcept {return internal_repository != nullptr;} - - // Manifest data. - // - package_id id; - upstream_version version; - priority_type priority; - string summary; - license_alternatives_type license_alternatives; - strings tags; - optional description; - string changes; - url_type url; - optional package_url; - email_type email; - optional package_email; - optional build_email; - dependencies_type dependencies; - requirements_type requirements; - lazy_shared_ptr internal_repository; - - // Path to the package file. Present only for internal packages. - // - optional location; - - // Present only for internal packages. - // - optional sha256sum; - - vector> other_repositories; - - // Database mapping. - // - #pragma db member(id) id column("") - #pragma db member(version) set(this.version.init (this.id.version, (?))) - - // license - // - using _license_key = odb::nested_key; - using _licenses_type = std::map<_license_key, string>; - - #pragma db value(_license_key) - #pragma db member(_license_key::outer) column("alternative_index") - #pragma db member(_license_key::inner) column("index") - - #pragma db member(license_alternatives) id_column("") value_column("") - #pragma db member(licenses) \ - virtual(_licenses_type) \ - after(license_alternatives) \ - get(odb::nested_get (this.license_alternatives)) \ - set(odb::nested_set (this.license_alternatives, std::move (?))) \ - id_column("") key_column("") value_column("license") - - // tags - // - #pragma db member(tags) id_column("") value_column("tag") - - // dependencies - // - using _dependency_key = odb::nested_key; - using _dependency_alternatives_type = - std::map<_dependency_key, dependency>; - - #pragma db value(_dependency_key) - #pragma db member(_dependency_key::outer) column("dependency_index") - #pragma db member(_dependency_key::inner) column("index") - - #pragma db member(dependencies) id_column("") value_column("") - #pragma db member(dependency_alternatives) \ - virtual(_dependency_alternatives_type) \ - after(dependencies) \ - get(odb::nested_get (this.dependencies)) \ - set(odb::nested_set (this.dependencies, std::move (?))) \ - id_column("") key_column("") value_column("dep_") - - // requirements - // - using _requirement_key = odb::nested_key; - using _requirement_alternatives_type = - std::map<_requirement_key, string>; - - #pragma db value(_requirement_key) - #pragma db member(_requirement_key::outer) column("requirement_index") - #pragma db member(_requirement_key::inner) column("index") - - #pragma db member(requirements) id_column("") value_column("") - #pragma db member(requirement_alternatives) \ - virtual(_requirement_alternatives_type) \ - after(requirements) \ - get(odb::nested_get (this.requirements)) \ - set(odb::nested_set (this.requirements, std::move (?))) \ - id_column("") key_column("") value_column("id") - - // other_repositories - // - #pragma db member(other_repositories) \ - id_column("") value_column("repository") value_not_null - - // search_index - // - #pragma db member(search_index) virtual(weighted_text) null \ - access(search_text) - - #pragma db index method("GIN") member(search_index) - - private: - friend class odb::access; - package () = default; - - // Save keywords, summary, description, and changes to weighted_text - // a, b, c, d members, respectively. So a word found in keywords will - // have a higher weight than if it's found in the summary. - // - weighted_text - search_text () const; - - // Noop as search_index is a write-only member. - // - void - search_text (const weighted_text&) {} - }; - - // Package search query matching rank. - // - #pragma db view query("/*CALL*/ SELECT * FROM search_latest_packages(?)") - struct latest_package_search_rank - { - package_id id; - double rank; - }; - - #pragma db view \ - query("/*CALL*/ SELECT count(*) FROM search_latest_packages(?)") - struct latest_package_count - { - size_t result; - - operator size_t () const {return result;} - }; - - #pragma db view query("/*CALL*/ SELECT * FROM search_packages(?)") - struct package_search_rank - { - package_id id; - double rank; - }; - - #pragma db view query("/*CALL*/ SELECT count(*) FROM search_packages(?)") - struct package_count - { - size_t result; - - operator size_t () const {return result;} - }; - - #pragma db view query("/*CALL*/ SELECT * FROM latest_package(?)") - struct latest_package - { - package_id id; - }; - - #pragma db view object(package) - struct package_version - { - package_id id; - upstream_version version; - - // Database mapping. - // - #pragma db member(id) column("") - #pragma db member(version) set(this.version.init (this.id.version, (?))) - }; -} - -#endif // BREP_PACKAGE diff --git a/brep/package-extra.sql b/brep/package-extra.sql deleted file mode 100644 index 823c3af..0000000 --- a/brep/package-extra.sql +++ /dev/null @@ -1,130 +0,0 @@ --- This file should be parsable by the brep-migrate utility. To decrease the --- parser complexity, the following restrictions are placed: --- --- * comments must start with -- at the beginning of the line (ignoring --- leading spaces) --- * only CREATE and DROP statements for FUNCTION and TYPE --- * function bodies must be defined using $$-quoted strings --- * strings other then function bodies must be quoted with ' or " --- * statements must end with ";\n" --- - --- There is no need to drop to_tsvector() explicitly, as we can rely on "DROP --- TYPE IF EXISTS weighted_text CASCADE" statement below, which will drop all --- objects that depend on this type. Moreover this DROP FUNCTION statement will --- fail for old versions of PostgreSQL (experienced for 9.2.14) with error: --- type "weighted_text" does not exist. --- --- DROP FUNCTION IF EXISTS to_tsvector(IN document weighted_text); --- -DROP FUNCTION IF EXISTS search_packages(IN query tsquery, INOUT name TEXT); -DROP FUNCTION IF EXISTS search_latest_packages(IN query tsquery); -DROP FUNCTION IF EXISTS latest_package(INOUT name TEXT); -DROP FUNCTION IF EXISTS latest_packages(); - -DROP TYPE IF EXISTS weighted_text CASCADE; -CREATE TYPE weighted_text AS (a TEXT, b TEXT, c TEXT, d TEXT); - --- Return the latest versions of internal packages as a set of package rows. --- -CREATE FUNCTION -latest_packages() -RETURNS SETOF package AS $$ - SELECT p1.* - FROM package p1 LEFT JOIN package p2 ON ( - p1.internal_repository IS NOT NULL AND p1.name = p2.name AND - p2.internal_repository IS NOT NULL AND - (p1.version_epoch < p2.version_epoch OR - p1.version_epoch = p2.version_epoch AND - (p1.version_canonical_upstream < p2.version_canonical_upstream OR - p1.version_canonical_upstream = p2.version_canonical_upstream AND - (p1.version_canonical_release < p2.version_canonical_release OR - p1.version_canonical_release = p2.version_canonical_release AND - p1.version_revision < p2.version_revision)))) - WHERE - p1.internal_repository IS NOT NULL AND p2.name IS NULL; -$$ LANGUAGE SQL STABLE; - --- Find the latest version of an internal package having the specified name. --- Return a single row containing the package id, empty row set if the package --- not found. --- -CREATE FUNCTION -latest_package(INOUT name TEXT, - OUT version_epoch INTEGER, - OUT version_canonical_upstream TEXT, - OUT version_canonical_release TEXT, - OUT version_revision INTEGER) -RETURNS SETOF record AS $$ - SELECT name, version_epoch, version_canonical_upstream, - version_canonical_release, version_revision - FROM latest_packages() - WHERE name = latest_package.name; -$$ LANGUAGE SQL STABLE; - --- Search for the latest version of an internal packages matching the specified --- search query. Return a set of rows containing the package id and search --- rank. If query is NULL, then match all packages and return 0 rank for --- all rows. --- -CREATE FUNCTION -search_latest_packages(IN query tsquery, - OUT name TEXT, - OUT version_epoch INTEGER, - OUT version_canonical_upstream TEXT, - OUT version_canonical_release TEXT, - OUT version_revision INTEGER, - OUT rank real) -RETURNS SETOF record AS $$ - SELECT name, version_epoch, version_canonical_upstream, - version_canonical_release, version_revision, - CASE - WHEN query IS NULL THEN 0 --- Weight mapping: D C B A - ELSE ts_rank_cd('{0.05, 0.2, 0.9, 1.0}', search_index, query) - END AS rank - FROM latest_packages() - WHERE query IS NULL OR search_index @@ query; -$$ LANGUAGE SQL STABLE; - --- Search for packages matching the search query and having the specified name. --- Return a set of rows containing the package id and search rank. If query --- is NULL, then match all packages and return 0 rank for all rows. --- -CREATE FUNCTION -search_packages(IN query tsquery, - INOUT name TEXT, - OUT version_epoch INTEGER, - OUT version_canonical_upstream TEXT, - OUT version_canonical_release TEXT, - OUT version_revision INTEGER, - OUT rank real) -RETURNS SETOF record AS $$ - SELECT name, version_epoch, version_canonical_upstream, - version_canonical_release, version_revision, - CASE - WHEN query IS NULL THEN 0 --- Weight mapping: D C B A - ELSE ts_rank_cd('{0.05, 0.2, 0.9, 1.0}', search_index, query) - END AS rank - FROM package - WHERE - internal_repository IS NOT NULL AND name = search_packages.name AND - (query IS NULL OR search_index @@ query); -$$ LANGUAGE SQL STABLE; - --- Parse weighted_text to tsvector. --- -CREATE FUNCTION -to_tsvector(IN document weighted_text) -RETURNS tsvector AS $$ - SELECT - CASE - WHEN document IS NULL THEN NULL - ELSE - setweight(to_tsvector(document.a), 'A') || - setweight(to_tsvector(document.b), 'B') || - setweight(to_tsvector(document.c), 'C') || - setweight(to_tsvector(document.d), 'D') - END -$$ LANGUAGE SQL IMMUTABLE; diff --git a/brep/package-traits b/brep/package-traits deleted file mode 100644 index f6bcc6e..0000000 --- a/brep/package-traits +++ /dev/null @@ -1,38 +0,0 @@ -// file : brep/package-traits -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BREP_PACKAGE_TRAITS -#define BREP_PACKAGE_TRAITS - -#include // size_t - -#include - -#include // weighted_text - -namespace odb -{ - namespace pgsql - { - template <> - class value_traits - { - public: - typedef brep::weighted_text value_type; - typedef value_type query_type; - typedef details::buffer image_type; - - static void - set_value (value_type&, const details::buffer&, std::size_t, bool) {} - - static void - set_image (details::buffer&, - std::size_t& n, - bool& is_null, - const value_type&); - }; - } -} - -#endif // BREP_PACKAGE_TRAITS diff --git a/brep/package-traits.cxx b/brep/package-traits.cxx deleted file mode 100644 index 8cdcf65..0000000 --- a/brep/package-traits.cxx +++ /dev/null @@ -1,70 +0,0 @@ -// file : brep/package-traits.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include -#include -#include -#include // memcpy - -#include - -using namespace std; - -namespace odb -{ - namespace pgsql - { - static inline void - to_pg_string (ostream& os, const string& s) - { - os << '"'; - - for (auto c: s) - { - if (c == '\\' || c == '"') - os << '\\'; - - os << c; - } - - os << '"'; - } - - // Convert C++ weighted_text struct to PostgreSQL weighted_text - // composite type. - // - void value_traits:: - set_image (details::buffer& b, - size_t& n, - bool& is_null, - const value_type& v) - { - is_null = v.a.empty () && v.b.empty () && v.c.empty () && v.d.empty (); - - if (!is_null) - { - ostringstream o; - o << "("; - to_pg_string (o, v.a); - o << ","; - to_pg_string (o, v.b); - o << ","; - to_pg_string (o, v.c); - o << ","; - to_pg_string (o, v.d); - o << ")"; - - const string& s (o.str ()); - n = s.size (); - - if (n > b.capacity ()) - b.capacity (n); - - memcpy (b.data (), s.c_str (), n); - } - } - } -} diff --git a/brep/package.cxx b/brep/package.cxx deleted file mode 100644 index 144fc05..0000000 --- a/brep/package.cxx +++ /dev/null @@ -1,169 +0,0 @@ -// file : brep/package.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include - -#include - -using namespace std; -using namespace odb::core; - -namespace brep -{ - // dependency - // - string dependency:: - name () const - { - return package.object_id ().name; - } - - ostream& - operator<< (ostream& o, const dependency& d) - { - o << d.name (); - - if (d.constraint) - o << ' ' << *d.constraint; - - return o; - } - - bool - operator== (const dependency& x, const dependency& y) - { - return x.name () == y.name () && x.constraint == y.constraint; - } - - bool - operator!= (const dependency& x, const dependency& y) - { - return !(x == y); - } - - // package - // - package:: - package (string nm, - version_type vr, - priority_type pr, - string sm, - license_alternatives_type la, - strings tg, - optional ds, - string ch, - url_type ur, - optional pu, - email_type em, - optional pe, - optional be, - dependencies_type dp, - requirements_type rq, - optional lc, - optional sh, - shared_ptr rp) - : id (move (nm), vr), - version (move (vr)), - priority (move (pr)), - summary (move (sm)), - license_alternatives (move (la)), - tags (move (tg)), - description (move (ds)), - changes (move (ch)), - url (move (ur)), - package_url (move (pu)), - email (move (em)), - package_email (move (pe)), - build_email (move (be)), - dependencies (move (dp)), - requirements (move (rq)), - internal_repository (move (rp)), - location (move (lc)), - sha256sum (move (sh)) - { - assert (internal_repository->internal); - } - - package:: - package (string nm, - version_type vr, - shared_ptr rp) - : id (move (nm), vr), - version (move (vr)) - { - assert (!rp->internal); - other_repositories.emplace_back (move (rp)); - } - - weighted_text package:: - search_text () const - { - if (!internal ()) - return weighted_text (); - - // Derive keywords from the basic package information: name, - // version. - // - //@@ What about 'stable' from cppget.org/stable? Add path of - // the repository to keywords? Or is it too "polluting" and - // we will handle it in some other way (e.g., by allowing - // the user to specify repo location in the drop-down box)? - // Probably drop-box would be better as also tells what are - // the available internal repositories. - // - string k (id.name + " " + version.string () + " " + version.string (true)); - - // Add tags to keywords. - // - for (const auto& t: tags) - k += " " + t; - - // Add licenses to keywords. - // - for (const auto& la: license_alternatives) - { - for (const auto& l: la) - { - k += " " + l; - - // If license is say LGPLv2 then LGPL is also a keyword. - // - size_t n (l.size ()); - if (n > 2 && l[n - 2] == 'v' && l[n - 1] >= '0' && l[n - 1] <= '9') - k += " " + string (l, 0, n - 2); - } - } - - return {move (k), summary, description ? *description : "", changes}; - } - - // repository - // - repository:: - repository (repository_location l, - string d, - repository_location h, - optional c, - uint16_t r) - : name (l.canonical_name ()), - location (move (l)), - display_name (move (d)), - priority (r), - cache_location (move (h)), - certificate (move (c)), - internal (true) - { - } - - repository:: - repository (repository_location l) - : name (l.canonical_name ()), - location (move (l)), - priority (0), - internal (false) - { - } -} diff --git a/brep/package.xml b/brep/package.xml deleted file mode 100644 index 657c2fc..0000000 --- a/brep/package.xml +++ /dev/null @@ -1,417 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
diff --git a/brep/types b/brep/types deleted file mode 100644 index fb036c4..0000000 --- a/brep/types +++ /dev/null @@ -1,94 +0,0 @@ -// file : brep/types -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BREP_TYPES -#define BREP_TYPES - -#include -#include -#include // unique_ptr, shared_ptr -#include // pair -#include // size_t, nullptr_t -#include // uint{8,16,32,64}_t -#include -#include -#include // function, reference_wrapper - -#include // ios_base::failure -#include // exception -#include // logic_error, invalid_argument, runtime_error -#include - -#include - -#include -#include -#include -#include - -namespace brep -{ - // Commonly-used types. - // - using std::uint8_t; - using std::uint16_t; - using std::uint32_t; - using std::uint64_t; - - using std::size_t; - using std::nullptr_t; - - using std::pair; - using std::string; - using std::function; - using std::reference_wrapper; - - using std::unique_ptr; - using std::shared_ptr; - using std::weak_ptr; - - using std::vector; - - using strings = vector; - using cstrings = vector; - - using std::istream; - using std::ostream; - - // Exceptions. While is included, there is no using for - // std::exception -- use qualified. - // - using std::logic_error; - using std::invalid_argument; - using std::runtime_error; - using std::system_error; - using io_error = std::ios_base::failure; - - // - // - using butl::optional; - using butl::nullopt; - - // ODB smart pointers. - // - using odb::lazy_shared_ptr; - using odb::lazy_weak_ptr; - - // - // - using butl::path; - using butl::dir_path; - using butl::basic_path; - using butl::invalid_path; - - using paths = std::vector; - using dir_paths = std::vector; - - // - // - using butl::timestamp; - using butl::timestamp_nonexistent; -} - -#endif // BREP_TYPES diff --git a/brep/utility b/brep/utility deleted file mode 100644 index 3c6f5a2..0000000 --- a/brep/utility +++ /dev/null @@ -1,34 +0,0 @@ -// file : brep/utility -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BREP_UTILITY -#define BREP_UTILITY - -#include // make_shared() -#include // to_string() -#include // move(), forward(), declval(), make_pair() -#include // assert() -#include // make_move_iterator() - -#include // reverse_iterate(), operator<<(ostream, exception) - -namespace brep -{ - using std::move; - using std::forward; - using std::declval; - - using std::make_pair; - using std::make_shared; - using std::make_move_iterator; - using std::to_string; - - // - // - using butl::reverse_iterate; -} - -#include - -#endif // BREP_UTILITY diff --git a/brep/version.in b/brep/version.in deleted file mode 100644 index a5b236e..0000000 --- a/brep/version.in +++ /dev/null @@ -1,74 +0,0 @@ -// file : brep/version.in -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BREP_VERSION // Note: using the version macro itself. - -// Note: using build2 standard versioning scheme. The numeric version format -// is AAABBBCCCDDDE where: -// -// AAA - major version number -// BBB - minor version number -// CCC - bugfix version number -// DDD - alpha / beta (DDD + 500) version number -// E - final (0) / snapshot (1) -// -// When DDDE is not 0, 1 is subtracted from AAABBBCCC. For example: -// -// Version AAABBBCCCDDDE -// -// 0.1.0 0000010000000 -// 0.1.2 0000010010000 -// 1.2.3 0010020030000 -// 2.2.0-a.1 0020019990010 -// 3.0.0-b.2 0029999995020 -// 2.2.0-a.1.z 0020019990011 -// -#define BREP_VERSION $brep.version.project_number$ULL -#define BREP_VERSION_STR "$brep.version.project$" -#define BREP_VERSION_ID "$brep.version.project_id$" - -#define BREP_VERSION_MAJOR $brep.version.major$ -#define BREP_VERSION_MINOR $brep.version.minor$ -#define BREP_VERSION_PATCH $brep.version.patch$ - -#define BREP_PRE_RELEASE $brep.version.pre_release$ - -#define BREP_SNAPSHOT $brep.version.snapshot_sn$ULL -#define BREP_SNAPSHOT_ID "$brep.version.snapshot_id$" - -#include - -$libbutl.check(LIBBUTL_VERSION, LIBBUTL_SNAPSHOT)$ - -#include - -$libbpkg.check(LIBBPKG_VERSION, LIBBPKG_SNAPSHOT)$ - -#include - -$libbbot.check(LIBBBOT_VERSION, LIBBBOT_SNAPSHOT)$ - -#include - -$libodb.check(LIBODB_VERSION, LIBODB_SNAPSHOT)$ - -#include - -$libodb-pgsql.check(LIBODB_PGSQL_VERSION, LIBODB_PGSQL_SNAPSHOT)$ - -// @@ Not really the correct place for the check since we don't use -// it here. -// -/* -#include - -$libstudxml.check(LIBSTUDXML_VERSION, LIBSTUDXML_SNAPSHOT)$ -*/ - -// For now these are the same. -// -#define LIBBREP_VERSION BREP_VERSION -#define LIBBREP_VERSION_STR BREP_VERSION_STR - -#endif // BREP_VERSION diff --git a/brep/wrapper-traits b/brep/wrapper-traits deleted file mode 100644 index 4be84b7..0000000 --- a/brep/wrapper-traits +++ /dev/null @@ -1,63 +0,0 @@ -// file : brep/wrapper-traits -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BREP_WRAPPER_TRAITS -#define BREP_WRAPPER_TRAITS - -#include - -#include - -#include - -namespace odb -{ - template - class wrapper_traits> - { - public: - typedef T wrapped_type; - typedef butl::optional wrapper_type; - - // T can be const. - // - typedef - typename odb::details::meta::remove_const::result - unrestricted_wrapped_type; - - static const bool null_handler = true; - static const bool null_default = true; - - static bool - get_null (const wrapper_type& o) - { - return !o; - } - - static void - set_null (wrapper_type& o) - { - o = wrapper_type (); - } - - static const wrapped_type& - get_ref (const wrapper_type& o) - { - return *o; - } - - static unrestricted_wrapped_type& - set_ref (wrapper_type& o) - { - if (!o) - o = unrestricted_wrapped_type (); - - return const_cast (*o); - } - }; -} - -#include - -#endif // BREP_WRAPPER_TRAITS diff --git a/build/export.build b/build/export.build index ec930b1..0c38d80 100644 --- a/build/export.build +++ b/build/export.build @@ -4,7 +4,7 @@ $out_root/: { - include brep/ + include libbrep/ } -export $out_root/brep/lib{brep} +export $out_root/libbrep/lib{brep} diff --git a/build/root.build b/build/root.build index b273cae..d75dbcf 100644 --- a/build/root.build +++ b/build/root.build @@ -6,7 +6,7 @@ cxx.std = latest using cxx -hxx{*}: extension = +hxx{*}: extension = hxx ixx{*}: extension = ixx txx{*}: extension = txx cxx{*}: extension = cxx diff --git a/buildfile b/buildfile index 5be0e5b..25d38ea 100644 --- a/buildfile +++ b/buildfile @@ -2,7 +2,7 @@ # copyright : Copyright (c) 2014-2017 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file -./: brep/ mod/ load/ migrate/ tests/ www/ doc/ etc/ \ +./: libbrep/ mod/ load/ migrate/ tests/ www/ doc/ etc/ \ doc{INSTALL INSTALL-DEV LICENSE NEWS README version} file{manifest} doc{version}: file{manifest} # Generated by the version module. diff --git a/libbrep/.gitignore b/libbrep/.gitignore new file mode 100644 index 0000000..3f251f5 --- /dev/null +++ b/libbrep/.gitignore @@ -0,0 +1,10 @@ +common-odb.?xx + +package-odb.?xx +package.sql +package-extra.hxx + +build-odb.?xx +build.sql + +version.hxx diff --git a/libbrep/build.cxx b/libbrep/build.cxx new file mode 100644 index 0000000..710c0b2 --- /dev/null +++ b/libbrep/build.cxx @@ -0,0 +1,48 @@ +// file : libbrep/build.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +namespace brep +{ + // build_state + // + string + to_string (build_state s) + { + switch (s) + { + case build_state::untested: return "untested"; + case build_state::testing: return "testing"; + case build_state::tested: return "tested"; + } + + return string (); // Should never reach. + } + + build_state + to_build_state (const string& s) + { + if (s == "untested") return build_state::untested; + else if (s == "testing") return build_state::testing; + else if (s == "tested") return build_state::tested; + else throw invalid_argument ("invalid build state '" + s + "'"); + } + + // build + // + build:: + build (string pnm, version pvr, string cfg, string mnm, string msm) + : id (package_id (move (pnm), pvr), move (cfg)), + package_name (id.package.name), + package_version (move (pvr)), + configuration (id.configuration), + state (build_state::testing), + timestamp (timestamp_type::clock::now ()), + forced (false), + machine (move (mnm)), + machine_summary (move (msm)) + { + } +} diff --git a/libbrep/build.hxx b/libbrep/build.hxx new file mode 100644 index 0000000..d28f5ab --- /dev/null +++ b/libbrep/build.hxx @@ -0,0 +1,166 @@ +// file : libbrep/build.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_BUILD_HXX +#define LIBBREP_BUILD_HXX + +#include + +#include +#include + +#include + +#include +#include + +#include // Must be included last (see assert). + +// Used by the data migration entries. +// +#define LIBBREP_BUILD_SCHEMA_VERSION_BASE 1 + +#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 1, open) + +// We have to keep these mappings at the global scope instead of inside +// the brep namespace because they need to be also effective in the +// bbot namespace from which we "borrow" types (and some of them use the mapped +// types). +// +#pragma db map type(bbot::result_status) as(std::string) \ + to(to_string (?)) \ + from(bbot::to_result_status (?)) + +namespace brep +{ + #pragma db value + struct build_id + { + package_id package; + string configuration; + + build_id () = default; + build_id (package_id p, string c) + : package (move (p)), configuration (move (c)) {} + }; + + inline bool + operator< (const build_id& x, const build_id& y) + { + return + x.package < y.package ? true : + y.package < x.package ? false : + x.configuration < y.configuration; + } + + // build_state + // + enum class build_state: std::uint8_t + { + untested, + testing, + tested + }; + + string + to_string (build_state); + + build_state + to_build_state (const string&); // May throw invalid_argument. + + inline ostream& + operator<< (ostream& os, build_state s) {return os << to_string (s);} + + #pragma db map type(build_state) as(string) \ + to(to_string (?)) \ + from(brep::to_build_state (?)) + + // result_status + // + using bbot::result_status; + + using optional_result_status = optional; + + #pragma db map type(optional_result_status) as(optional_string) \ + to((?) ? bbot::to_string (*(?)) : brep::optional_string ()) \ + from((?) \ + ? bbot::to_result_status (*(?)) \ + : brep::optional_result_status ()) + + // operation_results + // + using bbot::operation_result; + #pragma db value(operation_result) definition + + using bbot::operation_results; + + #pragma db object pointer(shared_ptr) session + class build + { + public: + using timestamp_type = brep::timestamp; + + // Create the build object with the testing state, non-existent status, + // the timestamp set to now and the forced flag set to false. + // + build (string name, version, + string configuration, + string machine, string machine_summary); + + build_id id; + + string& package_name; // Tracks id.package.name. + upstream_version package_version; // Original of id.package.version. + string& configuration; // Tracks id.configuration. + + build_state state; + + // Time of the last state change (the creation time initially). + // + timestamp_type timestamp; + + // True if the package rebuild has been forced. + // + bool forced; + + // Present only if the state is 'tested'. + // + optional status; + + // Present only if the state is 'testing' or 'tested'. + // + optional machine; + optional machine_summary; + + // Note that the logs are stored as std::string/TEXT which is Ok since + // they are UTF-8 and our database is UTF-8. + // + #pragma db section(results_section) + operation_results results; + + #pragma db load(lazy) update(always) + odb::section results_section; + + // Database mapping. + // + #pragma db member(id) id column("") + + #pragma db member(package_name) transient + #pragma db member(package_version) \ + set(this.package_version.init (this.id.package.version, (?))) + #pragma db member(configuration) transient + + #pragma db member(results) id_column("") value_column("") + + build (const build&) = delete; + build& operator= (const build&) = delete; + + private: + friend class odb::access; + build () + : package_name (id.package.name), configuration (id.configuration) {} + }; +} + +#endif // LIBBREP_BUILD_HXX diff --git a/libbrep/build.xml b/libbrep/build.xml new file mode 100644 index 0000000..8d9178a --- /dev/null +++ b/libbrep/build.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
diff --git a/libbrep/buildfile b/libbrep/buildfile new file mode 100644 index 0000000..b4005ab --- /dev/null +++ b/libbrep/buildfile @@ -0,0 +1,50 @@ +# file : libbrep/buildfile +# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +define sql: file +sql{*}: extension = sql +sql{*}: install = data/ + +import int_libs = libodb%lib{odb} +import int_libs += libodb-pgsql%lib{odb-pgsql} +import int_libs += libbutl%lib{butl} +import int_libs += libbpkg%lib{bpkg} +import int_libs += libbbot%lib{bbot} + +lib{brep}: \ +{hxx cxx}{ build } \ +{file }{ build.xml } \ +{hxx ixx cxx}{ build-odb } \ +{hxx cxx}{ common } \ +{hxx ixx cxx}{ common-odb } \ +{hxx cxx}{ package } \ +{file }{ package.xml } \ +{hxx ixx cxx}{ package-odb } \ +{hxx }{ package-extra } \ +{hxx cxx}{ package-traits } \ +{hxx cxx}{ database-lock } \ +{hxx }{ types } \ +{hxx }{ utility } \ +{hxx }{ version } \ +{hxx }{ wrapper-traits } \ + $int_libs \ +sql{build package package-extra} + +hxx{version}: in{version} $src_root/file{manifest} +hxx{version}: dist = true + +# For pre-releases use the complete version to make sure they cannot be used +# in place of another pre-release or the final version. +# +if $version.pre_release + lib{brep}: bin.lib.version = @"-$version.project_id" +else + lib{brep}: bin.lib.version = @"-$version.major.$version.minor" + +lib{brep}: cxx.export.poptions = "-I$out_root" "-I$src_root" +lib{brep}: cxx.export.libs = $int_libs + +# Install into the libbrep/ subdirectory of, say, /usr/include/. +# +install.include = $install.include/libbrep/ diff --git a/libbrep/common.cxx b/libbrep/common.cxx new file mode 100644 index 0000000..39fd0c7 --- /dev/null +++ b/libbrep/common.cxx @@ -0,0 +1,10 @@ +// file : libbrep/common.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +namespace brep +{ + const version wildcard_version (0, "0", nullopt, 0); +} diff --git a/libbrep/common.hxx b/libbrep/common.hxx new file mode 100644 index 0000000..bf60491 --- /dev/null +++ b/libbrep/common.hxx @@ -0,0 +1,341 @@ +// file : libbrep/common.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_COMMON_HXX +#define LIBBREP_COMMON_HXX + +#include +#include +#include // static_assert + +#include +#include + +// The uint16_t value range is not fully covered by SMALLINT PostgreSQL type +// to which uint16_t is mapped by default. +// +#pragma db value(uint16_t) type("INTEGER") + +namespace brep +{ + // Use an image type to map bpkg::version to the database since there + // is no way to modify individual components directly. + // + #pragma db value + struct _version + { + uint16_t epoch; + string canonical_upstream; + string canonical_release; + uint16_t revision; + string upstream; + optional release; + }; +} + +#include + +namespace brep +{ + using optional_version = optional; + using _optional_version = optional<_version>; +} + +// Prevent assert() macro expansion in get/set expressions. This should +// appear after all #include directives since the assert() macro is +// redefined in each inclusion. +// +#ifdef ODB_COMPILER +# undef assert +# define assert assert +void assert (int); +#endif + +// We have to keep these mappings at the global scope instead of inside +// the brep namespace because they need to be also effective in the +// bpkg namespace from which we "borrow" types (and some of them use version). +// +#pragma db map type(bpkg::version) as(brep::_version) \ + to(brep::_version{(?).epoch, \ + (?).canonical_upstream, \ + (?).canonical_release, \ + (?).revision, \ + (?).upstream, \ + (?).release}) \ + from(bpkg::version ((?).epoch, \ + std::move ((?).upstream), \ + std::move ((?).release), \ + (?).revision)) + +#pragma db map type(brep::optional_version) as(brep::_optional_version) \ + to((?) \ + ? brep::_version{(?)->epoch, \ + (?)->canonical_upstream, \ + (?)->canonical_release, \ + (?)->revision, \ + (?)->upstream, \ + (?)->release} \ + : brep::_optional_version ()) \ + from((?) \ + ? bpkg::version ((?)->epoch, \ + std::move ((?)->upstream), \ + std::move ((?)->release), \ + (?)->revision) \ + : brep::optional_version ()) + +namespace brep +{ + // path + // + #pragma db map type(path) as(string) to((?).string ()) from(brep::path (?)) + + using optional_path = optional; + using optional_string = optional; + + #pragma db map type(optional_path) as(brep::optional_string) \ + to((?) ? (?)->string () : brep::optional_string ()) \ + from((?) ? brep::path (*(?)) : brep::optional_path ()) + + #pragma db map type(dir_path) as(string) \ + to((?).string ()) from(brep::dir_path (?)) + + // Ensure that timestamp can be represented in nonoseconds without loss of + // accuracy, so the following ODB mapping is adequate. + // + static_assert( + std::ratio_greater_equal::value, + "The following timestamp ODB mapping is invalid"); + + // As it pointed out in butl/timestamp we will overflow in year 2262, but + // by that time some larger basic type will be available for mapping. + // + #pragma db map type(timestamp) as(uint64_t) \ + to(std::chrono::duration_cast ( \ + (?).time_since_epoch ()).count ()) \ + from(brep::timestamp ( \ + std::chrono::duration_cast ( \ + std::chrono::nanoseconds (?)))) + + // version + // + using bpkg::version; + + #pragma db value + struct canonical_version + { + uint16_t epoch; + string canonical_upstream; + string canonical_release; + uint16_t revision; + + bool + empty () const noexcept + { + // Note that an empty canonical_upstream doesn't denote an empty + // canonical_version. Remeber, that canonical_upstream doesn't include + // rightmost digit-only zero components? So non-empty version("0") has + // an empty canonical_upstream. + // + return epoch == 0 && canonical_upstream.empty () && + canonical_release.empty () && revision == 0; + } + + // Change collation to ensure the proper comparison of the "absent" release + // with a specified one. + // + // The default collation for UTF8-encoded TEXT columns in PostgreSQL is + // UCA-compliant. This makes the statement 'a' < '~' to be false, which + // in turn makes the statement 2.1.alpha < 2.1 to be false as well. + // + // Unicode Collation Algorithm (UCA): http://unicode.org/reports/tr10/ + // + #pragma db member(canonical_release) options("COLLATE \"C\"") + }; + + #pragma db value transient + struct upstream_version: version + { + #pragma db member(upstream_) virtual(string) \ + get(this.upstream) \ + set(this = brep::version (0, std::move (?), std::string (), 0)) + + #pragma db member(release_) virtual(optional_string) \ + get(this.release) \ + set(this = brep::version ( \ + 0, std::move (this.upstream), std::move (?), 0)) + + upstream_version () = default; + upstream_version (version v): version (move (v)) {} + upstream_version& + operator= (version v) {version& b (*this); b = v; return *this;} + + void + init (const canonical_version& cv, const upstream_version& uv) + { + *this = version (cv.epoch, uv.upstream, uv.release, cv.revision); + assert (cv.canonical_upstream == canonical_upstream && + cv.canonical_release == canonical_release); + } + }; + + // Wildcard version. Satisfies any dependency constraint and is represented + // as 0+0 (which is also the "stub version"; since a real version is always + // greater than the stub version, we reuse it to signify a special case). + // + extern const version wildcard_version; + + #pragma db value + struct package_id + { + string name; + canonical_version version; + + package_id () = default; + package_id (string n, const brep::version& v) + : name (move (n)), + version { + v.epoch, v.canonical_upstream, v.canonical_release, v.revision} + { + } + }; + + // Version comparison operators. + // + // They allow comparing objects that have epoch, canonical_upstream, + // canonical_release, and revision data members. The idea is that this + // works for both query members of types version and canonical_version + // as well as for comparing canonical_version to version. + // + template + inline auto + compare_version_eq (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + // Since we don't quite know what T1 and T2 are (and where the resulting + // expression will run), let's not push our luck with something like + // (!revision || x.revision == y.revision). + // + auto r (x.epoch == y.epoch && + x.canonical_upstream == y.canonical_upstream && + x.canonical_release == y.canonical_release); + + return revision + ? r && x.revision == y.revision + : r; + } + + template + inline auto + compare_version_ne (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + auto r (x.epoch != y.epoch || + x.canonical_upstream != y.canonical_upstream || + x.canonical_release != y.canonical_release); + + return revision + ? r || x.revision != y.revision + : r; + } + + template + inline auto + compare_version_lt (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + auto r ( + x.epoch < y.epoch || + (x.epoch == y.epoch && x.canonical_upstream < y.canonical_upstream) || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release < y.canonical_release)); + + return revision + ? r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release == y.canonical_release && x.revision < y.revision) + : r; + } + + template + inline auto + compare_version_le (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + auto r ( + x.epoch < y.epoch || + (x.epoch == y.epoch && x.canonical_upstream < y.canonical_upstream)); + + return revision + ? r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release < y.canonical_release) || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release == y.canonical_release && x.revision <= y.revision) + : r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release <= y.canonical_release); + } + + template + inline auto + compare_version_gt (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + auto r ( + x.epoch > y.epoch || + (x.epoch == y.epoch && x.canonical_upstream > y.canonical_upstream) || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release > y.canonical_release)); + + return revision + ? r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release == y.canonical_release && x.revision > y.revision) + : r; + } + + template + inline auto + compare_version_ge (const T1& x, const T2& y, bool revision) + -> decltype (x.epoch == y.epoch) + { + auto r ( + x.epoch > y.epoch || + (x.epoch == y.epoch && x.canonical_upstream > y.canonical_upstream)); + + return revision + ? r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release > y.canonical_release) || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release == y.canonical_release && x.revision >= y.revision) + : r || + (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream && + x.canonical_release >= y.canonical_release); + } + + template + inline auto + order_by_version_desc (const T& x) -> //decltype ("ORDER BY" + x.epoch) + decltype (x.epoch == 0) + { + return "ORDER BY" + + x.epoch + "DESC," + + x.canonical_upstream + "DESC," + + x.canonical_release + "DESC," + + x.revision + "DESC"; + } + + inline bool + operator< (const package_id& x, const package_id& y) + { + if (int r = x.name.compare (y.name)) + return r < 0; + + return compare_version_lt (x.version, y.version, true); + } +} + +#endif // LIBBREP_COMMON_HXX diff --git a/libbrep/database-lock.cxx b/libbrep/database-lock.cxx new file mode 100644 index 0000000..c69c240 --- /dev/null +++ b/libbrep/database-lock.cxx @@ -0,0 +1,44 @@ +// file : libbrep/database-lock.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include +#include + +namespace brep +{ + using namespace odb::pgsql; + + database_lock:: + database_lock (database& db) + { + // Before locking the table make sure it exists. + // + { + transaction t (db.begin ()); + db.execute ("CREATE TABLE IF NOT EXISTS database_mutex ()"); + t.commit (); + } + + connection_ = db.connection (); + + // Don't make current. Will be rolled back in destructor. + // + transaction_.reset (new transaction (connection_->begin (), false)); + + try + { + connection_->execute ("LOCK TABLE database_mutex NOWAIT"); + } + catch (const database_exception& e) + { + if (e.sqlstate () == "55P03") // The table is already locked. + throw database_locked (); + + throw; + } + } +} diff --git a/libbrep/database-lock.hxx b/libbrep/database-lock.hxx new file mode 100644 index 0000000..60d57a4 --- /dev/null +++ b/libbrep/database-lock.hxx @@ -0,0 +1,43 @@ +// file : libbrep/database-lock.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_DATABASE_LOCK_HXX +#define LIBBREP_DATABASE_LOCK_HXX + +#include // database, transaction +#include + +#include +#include + +namespace brep +{ + struct database_locked: std::exception + { + virtual char const* + what () const throw () {return "database locked";} + }; + + // Try to "lock" the PostgreSQL database in the constructor and release the + // lock in the destructor. Throw database_locked if the database is already + // locked by someone else. May also throw odb::pgsql::database_exception. + // + // This mechanism is used by the brep loader and schema migration tool to + // make sure they don't step on each others toes. + // + // Note: movable but not copyable. + // + class database_lock + { + public: + explicit + database_lock (odb::pgsql::database&); + + private: + odb::pgsql::connection_ptr connection_; + unique_ptr transaction_; + }; +} + +#endif // LIBBREP_DATABASE_LOCK_HXX diff --git a/libbrep/odb.sh b/libbrep/odb.sh new file mode 100755 index 0000000..b794a2d --- /dev/null +++ b/libbrep/odb.sh @@ -0,0 +1,40 @@ +#! /usr/bin/env bash + +trap 'exit 1' ERR + +odb=odb +lib="\ +-I$HOME/work/odb/libodb-pgsql-default \ +-I$HOME/work/odb/libodb-pgsql \ +-I$HOME/work/odb/libodb-default \ +-I$HOME/work/odb/libodb" + +$odb $lib -d pgsql --std c++11 --generate-query \ + --odb-epilogue '#include ' \ + --hxx-prologue '#include ' \ + -DLIBODB_BUILD2 -DLIBODB_PGSQL_BUILD2 \ + -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \ + --hxx-suffix ".hxx" --include-with-brackets \ + --include-prefix libbrep --guard-prefix LIBBREP \ + common.hxx + +$odb $lib -d pgsql --std c++11 --generate-query --generate-schema \ + --schema-format sql --schema-format embedded --schema-name package \ + --odb-epilogue '#include ' \ + --hxx-prologue '#include ' \ + --generate-prepared -DLIBODB_BUILD2 -DLIBODB_PGSQL_BUILD2 \ + -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \ + --hxx-suffix ".hxx" --include-with-brackets \ + --include-prefix libbrep --guard-prefix LIBBREP \ + package.hxx + +xxd -i package-extra.hxx + +$odb $lib -d pgsql --std c++11 --generate-query --generate-schema \ + --schema-format sql --schema-format embedded --schema-name build \ + --odb-epilogue '#include ' \ + --generate-prepared -DLIBODB_BUILD2 -DLIBODB_PGSQL_BUILD2 \ + -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \ + --hxx-suffix ".hxx" --include-with-brackets \ + --include-prefix libbrep --guard-prefix LIBBREP \ + build.hxx diff --git a/libbrep/package-extra.sql b/libbrep/package-extra.sql new file mode 100644 index 0000000..823c3af --- /dev/null +++ b/libbrep/package-extra.sql @@ -0,0 +1,130 @@ +-- This file should be parsable by the brep-migrate utility. To decrease the +-- parser complexity, the following restrictions are placed: +-- +-- * comments must start with -- at the beginning of the line (ignoring +-- leading spaces) +-- * only CREATE and DROP statements for FUNCTION and TYPE +-- * function bodies must be defined using $$-quoted strings +-- * strings other then function bodies must be quoted with ' or " +-- * statements must end with ";\n" +-- + +-- There is no need to drop to_tsvector() explicitly, as we can rely on "DROP +-- TYPE IF EXISTS weighted_text CASCADE" statement below, which will drop all +-- objects that depend on this type. Moreover this DROP FUNCTION statement will +-- fail for old versions of PostgreSQL (experienced for 9.2.14) with error: +-- type "weighted_text" does not exist. +-- +-- DROP FUNCTION IF EXISTS to_tsvector(IN document weighted_text); +-- +DROP FUNCTION IF EXISTS search_packages(IN query tsquery, INOUT name TEXT); +DROP FUNCTION IF EXISTS search_latest_packages(IN query tsquery); +DROP FUNCTION IF EXISTS latest_package(INOUT name TEXT); +DROP FUNCTION IF EXISTS latest_packages(); + +DROP TYPE IF EXISTS weighted_text CASCADE; +CREATE TYPE weighted_text AS (a TEXT, b TEXT, c TEXT, d TEXT); + +-- Return the latest versions of internal packages as a set of package rows. +-- +CREATE FUNCTION +latest_packages() +RETURNS SETOF package AS $$ + SELECT p1.* + FROM package p1 LEFT JOIN package p2 ON ( + p1.internal_repository IS NOT NULL AND p1.name = p2.name AND + p2.internal_repository IS NOT NULL AND + (p1.version_epoch < p2.version_epoch OR + p1.version_epoch = p2.version_epoch AND + (p1.version_canonical_upstream < p2.version_canonical_upstream OR + p1.version_canonical_upstream = p2.version_canonical_upstream AND + (p1.version_canonical_release < p2.version_canonical_release OR + p1.version_canonical_release = p2.version_canonical_release AND + p1.version_revision < p2.version_revision)))) + WHERE + p1.internal_repository IS NOT NULL AND p2.name IS NULL; +$$ LANGUAGE SQL STABLE; + +-- Find the latest version of an internal package having the specified name. +-- Return a single row containing the package id, empty row set if the package +-- not found. +-- +CREATE FUNCTION +latest_package(INOUT name TEXT, + OUT version_epoch INTEGER, + OUT version_canonical_upstream TEXT, + OUT version_canonical_release TEXT, + OUT version_revision INTEGER) +RETURNS SETOF record AS $$ + SELECT name, version_epoch, version_canonical_upstream, + version_canonical_release, version_revision + FROM latest_packages() + WHERE name = latest_package.name; +$$ LANGUAGE SQL STABLE; + +-- Search for the latest version of an internal packages matching the specified +-- search query. Return a set of rows containing the package id and search +-- rank. If query is NULL, then match all packages and return 0 rank for +-- all rows. +-- +CREATE FUNCTION +search_latest_packages(IN query tsquery, + OUT name TEXT, + OUT version_epoch INTEGER, + OUT version_canonical_upstream TEXT, + OUT version_canonical_release TEXT, + OUT version_revision INTEGER, + OUT rank real) +RETURNS SETOF record AS $$ + SELECT name, version_epoch, version_canonical_upstream, + version_canonical_release, version_revision, + CASE + WHEN query IS NULL THEN 0 +-- Weight mapping: D C B A + ELSE ts_rank_cd('{0.05, 0.2, 0.9, 1.0}', search_index, query) + END AS rank + FROM latest_packages() + WHERE query IS NULL OR search_index @@ query; +$$ LANGUAGE SQL STABLE; + +-- Search for packages matching the search query and having the specified name. +-- Return a set of rows containing the package id and search rank. If query +-- is NULL, then match all packages and return 0 rank for all rows. +-- +CREATE FUNCTION +search_packages(IN query tsquery, + INOUT name TEXT, + OUT version_epoch INTEGER, + OUT version_canonical_upstream TEXT, + OUT version_canonical_release TEXT, + OUT version_revision INTEGER, + OUT rank real) +RETURNS SETOF record AS $$ + SELECT name, version_epoch, version_canonical_upstream, + version_canonical_release, version_revision, + CASE + WHEN query IS NULL THEN 0 +-- Weight mapping: D C B A + ELSE ts_rank_cd('{0.05, 0.2, 0.9, 1.0}', search_index, query) + END AS rank + FROM package + WHERE + internal_repository IS NOT NULL AND name = search_packages.name AND + (query IS NULL OR search_index @@ query); +$$ LANGUAGE SQL STABLE; + +-- Parse weighted_text to tsvector. +-- +CREATE FUNCTION +to_tsvector(IN document weighted_text) +RETURNS tsvector AS $$ + SELECT + CASE + WHEN document IS NULL THEN NULL + ELSE + setweight(to_tsvector(document.a), 'A') || + setweight(to_tsvector(document.b), 'B') || + setweight(to_tsvector(document.c), 'C') || + setweight(to_tsvector(document.d), 'D') + END +$$ LANGUAGE SQL IMMUTABLE; diff --git a/libbrep/package-traits.cxx b/libbrep/package-traits.cxx new file mode 100644 index 0000000..a03cbac --- /dev/null +++ b/libbrep/package-traits.cxx @@ -0,0 +1,70 @@ +// file : libbrep/package-traits.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include +#include +#include // memcpy + +#include + +using namespace std; + +namespace odb +{ + namespace pgsql + { + static inline void + to_pg_string (ostream& os, const string& s) + { + os << '"'; + + for (auto c: s) + { + if (c == '\\' || c == '"') + os << '\\'; + + os << c; + } + + os << '"'; + } + + // Convert C++ weighted_text struct to PostgreSQL weighted_text + // composite type. + // + void value_traits:: + set_image (details::buffer& b, + size_t& n, + bool& is_null, + const value_type& v) + { + is_null = v.a.empty () && v.b.empty () && v.c.empty () && v.d.empty (); + + if (!is_null) + { + ostringstream o; + o << "("; + to_pg_string (o, v.a); + o << ","; + to_pg_string (o, v.b); + o << ","; + to_pg_string (o, v.c); + o << ","; + to_pg_string (o, v.d); + o << ")"; + + const string& s (o.str ()); + n = s.size (); + + if (n > b.capacity ()) + b.capacity (n); + + memcpy (b.data (), s.c_str (), n); + } + } + } +} diff --git a/libbrep/package-traits.hxx b/libbrep/package-traits.hxx new file mode 100644 index 0000000..b626ea8 --- /dev/null +++ b/libbrep/package-traits.hxx @@ -0,0 +1,38 @@ +// file : brep/package-traits -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BREP_PACKAGE_TRAITS +#define BREP_PACKAGE_TRAITS + +#include // size_t + +#include + +#include // weighted_text + +namespace odb +{ + namespace pgsql + { + template <> + class value_traits + { + public: + typedef brep::weighted_text value_type; + typedef value_type query_type; + typedef details::buffer image_type; + + static void + set_value (value_type&, const details::buffer&, std::size_t, bool) {} + + static void + set_image (details::buffer&, + std::size_t& n, + bool& is_null, + const value_type&); + }; + } +} + +#endif // BREP_PACKAGE_TRAITS diff --git a/libbrep/package.cxx b/libbrep/package.cxx new file mode 100644 index 0000000..20be387 --- /dev/null +++ b/libbrep/package.cxx @@ -0,0 +1,169 @@ +// file : libbrep/package.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include + +#include + +using namespace std; +using namespace odb::core; + +namespace brep +{ + // dependency + // + string dependency:: + name () const + { + return package.object_id ().name; + } + + ostream& + operator<< (ostream& o, const dependency& d) + { + o << d.name (); + + if (d.constraint) + o << ' ' << *d.constraint; + + return o; + } + + bool + operator== (const dependency& x, const dependency& y) + { + return x.name () == y.name () && x.constraint == y.constraint; + } + + bool + operator!= (const dependency& x, const dependency& y) + { + return !(x == y); + } + + // package + // + package:: + package (string nm, + version_type vr, + priority_type pr, + string sm, + license_alternatives_type la, + strings tg, + optional ds, + string ch, + url_type ur, + optional pu, + email_type em, + optional pe, + optional be, + dependencies_type dp, + requirements_type rq, + optional lc, + optional sh, + shared_ptr rp) + : id (move (nm), vr), + version (move (vr)), + priority (move (pr)), + summary (move (sm)), + license_alternatives (move (la)), + tags (move (tg)), + description (move (ds)), + changes (move (ch)), + url (move (ur)), + package_url (move (pu)), + email (move (em)), + package_email (move (pe)), + build_email (move (be)), + dependencies (move (dp)), + requirements (move (rq)), + internal_repository (move (rp)), + location (move (lc)), + sha256sum (move (sh)) + { + assert (internal_repository->internal); + } + + package:: + package (string nm, + version_type vr, + shared_ptr rp) + : id (move (nm), vr), + version (move (vr)) + { + assert (!rp->internal); + other_repositories.emplace_back (move (rp)); + } + + weighted_text package:: + search_text () const + { + if (!internal ()) + return weighted_text (); + + // Derive keywords from the basic package information: name, + // version. + // + //@@ What about 'stable' from cppget.org/stable? Add path of + // the repository to keywords? Or is it too "polluting" and + // we will handle it in some other way (e.g., by allowing + // the user to specify repo location in the drop-down box)? + // Probably drop-box would be better as also tells what are + // the available internal repositories. + // + string k (id.name + " " + version.string () + " " + version.string (true)); + + // Add tags to keywords. + // + for (const auto& t: tags) + k += " " + t; + + // Add licenses to keywords. + // + for (const auto& la: license_alternatives) + { + for (const auto& l: la) + { + k += " " + l; + + // If license is say LGPLv2 then LGPL is also a keyword. + // + size_t n (l.size ()); + if (n > 2 && l[n - 2] == 'v' && l[n - 1] >= '0' && l[n - 1] <= '9') + k += " " + string (l, 0, n - 2); + } + } + + return {move (k), summary, description ? *description : "", changes}; + } + + // repository + // + repository:: + repository (repository_location l, + string d, + repository_location h, + optional c, + uint16_t r) + : name (l.canonical_name ()), + location (move (l)), + display_name (move (d)), + priority (r), + cache_location (move (h)), + certificate (move (c)), + internal (true) + { + } + + repository:: + repository (repository_location l) + : name (l.canonical_name ()), + location (move (l)), + priority (0), + internal (false) + { + } +} diff --git a/libbrep/package.hxx b/libbrep/package.hxx new file mode 100644 index 0000000..8164639 --- /dev/null +++ b/libbrep/package.hxx @@ -0,0 +1,500 @@ +// file : libbrep/package.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_PACKAGE_HXX +#define LIBBREP_PACKAGE_HXX + +#include +#include + +#include +#include + +#include +#include + +#include // Must be included last (see assert). + +// Used by the data migration entries. +// +#define LIBBREP_PACKAGE_SCHEMA_VERSION_BASE 4 + +#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 4, open) + +namespace brep +{ + // @@ Might make sense to put some heavy members (e.g., description, + // containers) into a separate section. + // + // @@ Not sure there is a benefit in making tags a full-blown container + // (i.e., a separate table). Maybe provide a mapping of vector + // to TEXT as a comma-separated list. + // + + // Forward declarations. + // + class repository; + class package; + + // priority + // + using bpkg::priority; + + #pragma db value(priority) definition + #pragma db member(priority::value) column("") + + // url + // + using bpkg::url; + + #pragma db value(url) definition + #pragma db member(url::value) virtual(string) before access(this) column("") + + // email + // + using bpkg::email; + + #pragma db value(email) definition + #pragma db member(email::value) virtual(string) before access(this) column("") + + // licenses + // + using bpkg::licenses; + using license_alternatives = vector; + + #pragma db value(licenses) definition + + // dependencies + // + using bpkg::dependency_constraint; + + #pragma db value(dependency_constraint) definition + + // Notes: + // + // 1. Will the package be always resolvable? What if it is in + // another repository (i.e., a "chained" third-party repo). + // The question is then whether we will load such "third- + // party packages" (i.e., packages that are not in our + // repository). If the answer is yes, then we can have + // a pointer here. If the answer is no, then we can't. + // Also, if the answer is yes, we probably don't need to + // load as much information as for "our own" packages. We + // also shouldn't be showing them in search results, etc. + // I think all we need is to know which repository this + // package comes from so that we can tell the user. How are + // we going to capture this? Poly hierarchy of packages? + // + // 2. I believe we don't need to use a weak pointer here since + // there should be no package dependency cycles (and therefore + // ownership cycles). + // + // 3. Actually there can be dependency cycle as dependency referes not to + // just a package but a specific version, so for the same pair of + // packages dependency for different versions can have an opposite + // directions. The possible solution is instead of a package we point + // to the earliest version that satisfies the constraint. But this + // approach requires to ensure no cycles exist before instantiating + // package objects which in presense of "foreign" packages can be + // tricky. Can stick to just a package name until get some clarity on + // "foreign" package resolution. + // + // 4. As we left just the package class the dependency resolution come to + // finding the best version matching package object. The question is + // if to resolve dependencies on the loading phase or in the WEB interface + // when required. The arguments in favour of doing that during loading + // phase are: + // + // - WEB interface get offloaded from a possibly expensive queries + // which otherwise have to be executed multiple times for the same + // dependency no matter the result would be the same. + // + // - No need to complicate persisted object model with repository + // relations otherwise required just for dependency resolution. + // + + #pragma db value + struct dependency + { + using package_type = brep::package; + + lazy_shared_ptr package; + optional constraint; + + // Prerequisite package name. + // + string + name () const; + + // Database mapping. + // + #pragma db member(package) column("") not_null + #pragma db member(constraint) column("") + }; + + ostream& + operator<< (ostream&, const dependency&); + + bool + operator== (const dependency&, const dependency&); + + bool + operator!= (const dependency&, const dependency&); + + #pragma db value + class dependency_alternatives: public vector + { + public: + bool conditional; + bool buildtime; + string comment; + + dependency_alternatives () = default; + dependency_alternatives (bool d, bool b, string c) + : conditional (d), buildtime (b), comment (move (c)) {} + }; + + using dependencies = vector; + + // requirements + // + using bpkg::requirement_alternatives; + using requirements = vector; + + #pragma db value(requirement_alternatives) definition + + // repository_location + // + using bpkg::repository_location; + + #pragma db map type(repository_location) as(string) \ + to((?).string ()) from(brep::repository_location (?)) + + #pragma db value + class certificate + { + public: + string fingerprint; // SHA256 fingerprint. + string name; // CN component of Subject. + string organization; // O component of Subject. + string email; // email: in Subject Alternative Name. + string pem; // PEM representation. + }; + + #pragma db object pointer(shared_ptr) session + class repository + { + public: + using email_type = brep::email; + using certificate_type = brep::certificate; + + // Create internal repository. + // + repository (repository_location, + string display_name, + repository_location cache_location, + optional, + uint16_t priority); + + // Create external repository. + // + explicit + repository (repository_location); + + string name; // Object id (canonical name). + repository_location location; + string display_name; + + // The order in the internal repositories configuration file, starting from + // 1. 0 for external repositories. + // + uint16_t priority; + + optional url; + + // Present only for internal repositories. + // + optional email; + optional summary; + optional description; + + // Location of the repository local cache. Non empty for internal + // repositories and external ones with a filesystem path location. + // + repository_location cache_location; + + // Present only for internal signed repositories. + // + optional certificate; + + // Initialized with timestamp_nonexistent by default. + // + timestamp packages_timestamp; + + // Initialized with timestamp_nonexistent by default. + // + timestamp repositories_timestamp; + + bool internal; + vector> complements; + vector> prerequisites; + + // Database mapping. + // + #pragma db member(name) id + + #pragma db member(location) \ + set(this.location = std::move (?); \ + assert (this.name == this.location.canonical_name ())) + + #pragma db member(complements) id_column("repository") \ + value_column("complement") value_not_null + + #pragma db member(prerequisites) id_column("repository") \ + value_column("prerequisite") value_not_null + + private: + friend class odb::access; + repository () = default; + }; + + // The 'to' expression calls the PostgreSQL to_tsvector(weighted_text) + // function overload (package-extra.sql). Since we are only interested + // in "write-only" members of this type, make the 'from' expression + // always return empty string (we still have to work the placeholder + // in to keep overprotective ODB happy). + // + #pragma db map type("tsvector") as("TEXT") \ + to("to_tsvector((?)::weighted_text)") from("COALESCE('',(?))") + + // C++ type for weighted PostgreSQL tsvector. + // + #pragma db value type("tsvector") + struct weighted_text + { + string a; + string b; + string c; + string d; + }; + + #pragma db object pointer(shared_ptr) session + class package + { + public: + using repository_type = brep::repository; + using version_type = brep::version; + using priority_type = brep::priority; + using license_alternatives_type = brep::license_alternatives; + using url_type = brep::url; + using email_type = brep::email; + using dependencies_type = brep::dependencies; + using requirements_type = brep::requirements; + + // Create internal package object. + // + package (string name, + version_type, + priority_type, + string summary, + license_alternatives_type, + strings tags, + optional description, + string changes, + url_type, + optional package_url, + email_type, + optional package_email, + optional build_email, + dependencies_type, + requirements_type, + optional location, + optional sha256sum, + shared_ptr); + + // Create external package object. + // + // External repository packages can appear on the WEB interface only in + // dependency list in the form of a link to the corresponding WEB page. + // The only package information required to compose such a link is the + // package name, version, and repository location. + // + package (string name, version_type, shared_ptr); + + bool + internal () const noexcept {return internal_repository != nullptr;} + + // Manifest data. + // + package_id id; + upstream_version version; + priority_type priority; + string summary; + license_alternatives_type license_alternatives; + strings tags; + optional description; + string changes; + url_type url; + optional package_url; + email_type email; + optional package_email; + optional build_email; + dependencies_type dependencies; + requirements_type requirements; + lazy_shared_ptr internal_repository; + + // Path to the package file. Present only for internal packages. + // + optional location; + + // Present only for internal packages. + // + optional sha256sum; + + vector> other_repositories; + + // Database mapping. + // + #pragma db member(id) id column("") + #pragma db member(version) set(this.version.init (this.id.version, (?))) + + // license + // + using _license_key = odb::nested_key; + using _licenses_type = std::map<_license_key, string>; + + #pragma db value(_license_key) + #pragma db member(_license_key::outer) column("alternative_index") + #pragma db member(_license_key::inner) column("index") + + #pragma db member(license_alternatives) id_column("") value_column("") + #pragma db member(licenses) \ + virtual(_licenses_type) \ + after(license_alternatives) \ + get(odb::nested_get (this.license_alternatives)) \ + set(odb::nested_set (this.license_alternatives, std::move (?))) \ + id_column("") key_column("") value_column("license") + + // tags + // + #pragma db member(tags) id_column("") value_column("tag") + + // dependencies + // + using _dependency_key = odb::nested_key; + using _dependency_alternatives_type = + std::map<_dependency_key, dependency>; + + #pragma db value(_dependency_key) + #pragma db member(_dependency_key::outer) column("dependency_index") + #pragma db member(_dependency_key::inner) column("index") + + #pragma db member(dependencies) id_column("") value_column("") + #pragma db member(dependency_alternatives) \ + virtual(_dependency_alternatives_type) \ + after(dependencies) \ + get(odb::nested_get (this.dependencies)) \ + set(odb::nested_set (this.dependencies, std::move (?))) \ + id_column("") key_column("") value_column("dep_") + + // requirements + // + using _requirement_key = odb::nested_key; + using _requirement_alternatives_type = + std::map<_requirement_key, string>; + + #pragma db value(_requirement_key) + #pragma db member(_requirement_key::outer) column("requirement_index") + #pragma db member(_requirement_key::inner) column("index") + + #pragma db member(requirements) id_column("") value_column("") + #pragma db member(requirement_alternatives) \ + virtual(_requirement_alternatives_type) \ + after(requirements) \ + get(odb::nested_get (this.requirements)) \ + set(odb::nested_set (this.requirements, std::move (?))) \ + id_column("") key_column("") value_column("id") + + // other_repositories + // + #pragma db member(other_repositories) \ + id_column("") value_column("repository") value_not_null + + // search_index + // + #pragma db member(search_index) virtual(weighted_text) null \ + access(search_text) + + #pragma db index method("GIN") member(search_index) + + private: + friend class odb::access; + package () = default; + + // Save keywords, summary, description, and changes to weighted_text + // a, b, c, d members, respectively. So a word found in keywords will + // have a higher weight than if it's found in the summary. + // + weighted_text + search_text () const; + + // Noop as search_index is a write-only member. + // + void + search_text (const weighted_text&) {} + }; + + // Package search query matching rank. + // + #pragma db view query("/*CALL*/ SELECT * FROM search_latest_packages(?)") + struct latest_package_search_rank + { + package_id id; + double rank; + }; + + #pragma db view \ + query("/*CALL*/ SELECT count(*) FROM search_latest_packages(?)") + struct latest_package_count + { + size_t result; + + operator size_t () const {return result;} + }; + + #pragma db view query("/*CALL*/ SELECT * FROM search_packages(?)") + struct package_search_rank + { + package_id id; + double rank; + }; + + #pragma db view query("/*CALL*/ SELECT count(*) FROM search_packages(?)") + struct package_count + { + size_t result; + + operator size_t () const {return result;} + }; + + #pragma db view query("/*CALL*/ SELECT * FROM latest_package(?)") + struct latest_package + { + package_id id; + }; + + #pragma db view object(package) + struct package_version + { + package_id id; + upstream_version version; + + // Database mapping. + // + #pragma db member(id) column("") + #pragma db member(version) set(this.version.init (this.id.version, (?))) + }; +} + +#endif // LIBBREP_PACKAGE_HXX diff --git a/libbrep/package.xml b/libbrep/package.xml new file mode 100644 index 0000000..657c2fc --- /dev/null +++ b/libbrep/package.xml @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
diff --git a/libbrep/types.hxx b/libbrep/types.hxx new file mode 100644 index 0000000..a0001a7 --- /dev/null +++ b/libbrep/types.hxx @@ -0,0 +1,94 @@ +// file : libbrep/types.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_TYPES_HXX +#define LIBBREP_TYPES_HXX + +#include +#include +#include // unique_ptr, shared_ptr +#include // pair +#include // size_t, nullptr_t +#include // uint{8,16,32,64}_t +#include +#include +#include // function, reference_wrapper + +#include // ios_base::failure +#include // exception +#include // logic_error, invalid_argument, runtime_error +#include + +#include + +#include +#include +#include +#include + +namespace brep +{ + // Commonly-used types. + // + using std::uint8_t; + using std::uint16_t; + using std::uint32_t; + using std::uint64_t; + + using std::size_t; + using std::nullptr_t; + + using std::pair; + using std::string; + using std::function; + using std::reference_wrapper; + + using std::unique_ptr; + using std::shared_ptr; + using std::weak_ptr; + + using std::vector; + + using strings = vector; + using cstrings = vector; + + using std::istream; + using std::ostream; + + // Exceptions. While is included, there is no using for + // std::exception -- use qualified. + // + using std::logic_error; + using std::invalid_argument; + using std::runtime_error; + using std::system_error; + using io_error = std::ios_base::failure; + + // + // + using butl::optional; + using butl::nullopt; + + // ODB smart pointers. + // + using odb::lazy_shared_ptr; + using odb::lazy_weak_ptr; + + // + // + using butl::path; + using butl::dir_path; + using butl::basic_path; + using butl::invalid_path; + + using paths = std::vector; + using dir_paths = std::vector; + + // + // + using butl::timestamp; + using butl::timestamp_nonexistent; +} + +#endif // LIBBREP_TYPES_HXX diff --git a/libbrep/utility.hxx b/libbrep/utility.hxx new file mode 100644 index 0000000..f5948dd --- /dev/null +++ b/libbrep/utility.hxx @@ -0,0 +1,34 @@ +// file : libbrep/utility.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_UTILITY_HXX +#define LIBBREP_UTILITY_HXX + +#include // make_shared() +#include // to_string() +#include // move(), forward(), declval(), make_pair() +#include // assert() +#include // make_move_iterator() + +#include // reverse_iterate(), operator<<(ostream, exception) + +namespace brep +{ + using std::move; + using std::forward; + using std::declval; + + using std::make_pair; + using std::make_shared; + using std::make_move_iterator; + using std::to_string; + + // + // + using butl::reverse_iterate; +} + +#include + +#endif // LIBBREP_UTILITY_HXX diff --git a/libbrep/version.hxx.in b/libbrep/version.hxx.in new file mode 100644 index 0000000..e1b60a2 --- /dev/null +++ b/libbrep/version.hxx.in @@ -0,0 +1,74 @@ +// file : libbrep/version.hxx.in -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BREP_VERSION // Note: using the version macro itself. + +// Note: using build2 standard versioning scheme. The numeric version format +// is AAABBBCCCDDDE where: +// +// AAA - major version number +// BBB - minor version number +// CCC - bugfix version number +// DDD - alpha / beta (DDD + 500) version number +// E - final (0) / snapshot (1) +// +// When DDDE is not 0, 1 is subtracted from AAABBBCCC. For example: +// +// Version AAABBBCCCDDDE +// +// 0.1.0 0000010000000 +// 0.1.2 0000010010000 +// 1.2.3 0010020030000 +// 2.2.0-a.1 0020019990010 +// 3.0.0-b.2 0029999995020 +// 2.2.0-a.1.z 0020019990011 +// +#define BREP_VERSION $brep.version.project_number$ULL +#define BREP_VERSION_STR "$brep.version.project$" +#define BREP_VERSION_ID "$brep.version.project_id$" + +#define BREP_VERSION_MAJOR $brep.version.major$ +#define BREP_VERSION_MINOR $brep.version.minor$ +#define BREP_VERSION_PATCH $brep.version.patch$ + +#define BREP_PRE_RELEASE $brep.version.pre_release$ + +#define BREP_SNAPSHOT $brep.version.snapshot_sn$ULL +#define BREP_SNAPSHOT_ID "$brep.version.snapshot_id$" + +#include + +$libbutl.check(LIBBUTL_VERSION, LIBBUTL_SNAPSHOT)$ + +#include + +$libbpkg.check(LIBBPKG_VERSION, LIBBPKG_SNAPSHOT)$ + +#include + +$libbbot.check(LIBBBOT_VERSION, LIBBBOT_SNAPSHOT)$ + +#include + +$libodb.check(LIBODB_VERSION, LIBODB_SNAPSHOT)$ + +#include + +$libodb-pgsql.check(LIBODB_PGSQL_VERSION, LIBODB_PGSQL_SNAPSHOT)$ + +// @@ Not really the correct place for the check since we don't use +// it here. +// +/* +#include + +$libstudxml.check(LIBSTUDXML_VERSION, LIBSTUDXML_SNAPSHOT)$ +*/ + +// For now these are the same. +// +#define LIBBREP_VERSION BREP_VERSION +#define LIBBREP_VERSION_STR BREP_VERSION_STR + +#endif // BREP_VERSION diff --git a/libbrep/wrapper-traits.hxx b/libbrep/wrapper-traits.hxx new file mode 100644 index 0000000..ac039e3 --- /dev/null +++ b/libbrep/wrapper-traits.hxx @@ -0,0 +1,63 @@ +// file : libbrep/wrapper-traits.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBREP_WRAPPER_TRAITS_HXX +#define LIBBREP_WRAPPER_TRAITS_HXX + +#include + +#include + +#include + +namespace odb +{ + template + class wrapper_traits> + { + public: + typedef T wrapped_type; + typedef butl::optional wrapper_type; + + // T can be const. + // + typedef + typename odb::details::meta::remove_const::result + unrestricted_wrapped_type; + + static const bool null_handler = true; + static const bool null_default = true; + + static bool + get_null (const wrapper_type& o) + { + return !o; + } + + static void + set_null (wrapper_type& o) + { + o = wrapper_type (); + } + + static const wrapped_type& + get_ref (const wrapper_type& o) + { + return *o; + } + + static unrestricted_wrapped_type& + set_ref (wrapper_type& o) + { + if (!o) + o = unrestricted_wrapped_type (); + + return const_cast (*o); + } + }; +} + +#include + +#endif // LIBBREP_WRAPPER_TRAITS_HXX diff --git a/load/buildfile b/load/buildfile index 530dc72..e377a8e 100644 --- a/load/buildfile +++ b/load/buildfile @@ -7,13 +7,13 @@ import libs += libodb-pgsql%lib{odb-pgsql} import libs += libbutl%lib{butl} import libs += libbpkg%lib{bpkg} -include ../brep/ +include ../libbrep/ exe{brep-load}: \ { cxx}{ load } \ {hxx ixx cxx}{ load-options } \ {hxx cxx}{ types-parsers } \ -../brep/lib{brep} $libs +../libbrep/lib{brep} $libs # Generated options parser. # @@ -22,8 +22,8 @@ if $cli.configured cli.cxx{load-options}: cli{load} cli.options += -I $src_root --include-with-brackets --include-prefix load \ ---guard-prefix LOAD --generate-specifier --page-usage print_ --ansi-color \ ---cxx-prologue "#include " \ +--guard-prefix LOAD --generate-specifier --page-usage print_ --ansi-color \ +--cxx-prologue "#include " \ --long-usage # Include generated cli files into the distribution. diff --git a/load/load.cli b/load/load.cli index fe05ad2..51a34c3 100644 --- a/load/load.cli +++ b/load/load.cli @@ -6,7 +6,7 @@ include ; include ; include ; // uint16_t -include ; +include ; "\section=1" "\name=brep-load" diff --git a/load/load.cxx b/load/load.cxx index 3e9526f..3888c0f 100644 --- a/load/load.cxx +++ b/load/load.cxx @@ -27,14 +27,14 @@ #include -#include -#include +#include +#include -#include -#include -#include +#include +#include +#include -#include +#include using namespace std; using namespace odb::core; diff --git a/load/types-parsers b/load/types-parsers deleted file mode 100644 index 96acc07..0000000 --- a/load/types-parsers +++ /dev/null @@ -1,28 +0,0 @@ -// file : load/types-parsers -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -// CLI parsers, included into the generated source files. -// - -#ifndef BREP_LOAD_TYPES_PARSERS -#define BREP_LOAD_TYPES_PARSERS - -#include - -namespace cli -{ - class scanner; - - template - struct parser; - - template <> - struct parser - { - static void - parse (brep::path&, bool&, scanner&); - }; -} - -#endif // BREP_LOAD_TYPES_PARSERS diff --git a/load/types-parsers.cxx b/load/types-parsers.cxx index c388bda..c3d895f 100644 --- a/load/types-parsers.cxx +++ b/load/types-parsers.cxx @@ -2,9 +2,9 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include -#include // cli namespace +#include // cli namespace using namespace brep; diff --git a/load/types-parsers.hxx b/load/types-parsers.hxx new file mode 100644 index 0000000..b52c107 --- /dev/null +++ b/load/types-parsers.hxx @@ -0,0 +1,28 @@ +// file : load/types-parsers.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +// CLI parsers, included into the generated source files. +// + +#ifndef BREP_LOAD_TYPES_PARSERS_HXX +#define BREP_LOAD_TYPES_PARSERS_HXX + +#include + +namespace cli +{ + class scanner; + + template + struct parser; + + template <> + struct parser + { + static void + parse (brep::path&, bool&, scanner&); + }; +} + +#endif // BREP_LOAD_TYPES_PARSERS_HXX diff --git a/migrate/buildfile b/migrate/buildfile index 6b2ddd1..0c8fc97 100644 --- a/migrate/buildfile +++ b/migrate/buildfile @@ -6,12 +6,12 @@ import libs = libodb%lib{odb} import libs += libodb-pgsql%lib{odb-pgsql} import libs += libbutl%lib{butl} -include ../brep/ +include ../libbrep/ exe{brep-migrate}: \ { cxx}{ migrate } \ {hxx ixx cxx}{ migrate-options } \ -../brep/lib{brep} $libs +../libbrep/lib{brep} $libs # Generated options parser. # @@ -19,9 +19,9 @@ if $cli.configured { cli.cxx{migrate-options}: cli{migrate} - cli.options += -I $src_root --include-with-brackets --include-prefix migrate \ ---guard-prefix MIGRATE --generate-specifier --page-usage print_ --ansi-color \ ---long-usage + cli.options += -I $src_root --include-with-brackets \ +--include-prefix migrate --guard-prefix MIGRATE --generate-specifier \ +--page-usage print_ --ansi-color --long-usage # Include generated cli files into the distribution. # diff --git a/migrate/migrate.cxx b/migrate/migrate.cxx index e785531..047dfe0 100644 --- a/migrate/migrate.cxx +++ b/migrate/migrate.cxx @@ -15,12 +15,12 @@ #include -#include -#include +#include +#include -#include +#include -#include +#include using namespace std; using namespace odb::core; @@ -321,7 +321,7 @@ try if (create || drop) { static const char package_extras[] = { -#include +#include , '\0'}; schema s (db_schema == "package" diff --git a/mod/build-config b/mod/build-config deleted file mode 100644 index a5713d7..0000000 --- a/mod/build-config +++ /dev/null @@ -1,23 +0,0 @@ -// file : mod/build-config -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_BUILD_CONFIG -#define MOD_BUILD_CONFIG - -#include - -#include -#include - -namespace brep -{ - // Return pointer to the shared build configurations instance, creating one - // on the first call. Throw tab_parsing on parsing error, io_error on the - // underlying OS error. Is not thread-safe. - // - shared_ptr - shared_build_config (const path&); -} - -#endif // MOD_BUILD_CONFIG diff --git a/mod/build-config.cxx b/mod/build-config.cxx index 11be1b9..9cbd1bf 100644 --- a/mod/build-config.cxx +++ b/mod/build-config.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include diff --git a/mod/build-config.hxx b/mod/build-config.hxx new file mode 100644 index 0000000..13024dc --- /dev/null +++ b/mod/build-config.hxx @@ -0,0 +1,23 @@ +// file : mod/build-config.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_BUILD_CONFIG_HXX +#define MOD_BUILD_CONFIG_HXX + +#include + +#include +#include + +namespace brep +{ + // Return pointer to the shared build configurations instance, creating one + // on the first call. Throw tab_parsing on parsing error, io_error on the + // underlying OS error. Is not thread-safe. + // + shared_ptr + shared_build_config (const path&); +} + +#endif // MOD_BUILD_CONFIG_HXX diff --git a/mod/buildfile b/mod/buildfile index 771b52b..f845a42 100644 --- a/mod/buildfile +++ b/mod/buildfile @@ -16,7 +16,7 @@ import libs += libbutl%lib{butl} import libs += libbpkg%lib{bpkg} import libs += libbbot%lib{bbot} -include ../brep/ +include ../libbrep/ mod{brep}: \ {hxx cxx}{ build-config } \ @@ -46,7 +46,7 @@ mod{brep}: \ ../web/apache/{hxx ixx cxx}{ request } \ ../web/apache/{hxx txx cxx}{ service } \ ../web/apache/{hxx }{ stream } \ -../brep/lib{brep} $libs +../libbrep/lib{brep} $libs # Don't install any of the module's headers. # @@ -62,9 +62,9 @@ if $cli.configured # Set option prefix to the empty value to handle all unknown request # parameters uniformly with a single catch block. # - cli.options += --std c++11 -I $src_root --include-with-brackets \ ---include-prefix mod --guard-prefix MOD --generate-specifier \ ---cxx-prologue "#include " \ + cli.options += --std c++11 -I $src_root --include-with-brackets \ +--include-prefix mod --guard-prefix MOD --generate-specifier \ +--cxx-prologue "#include " \ --cli-namespace brep::cli --generate-file-scanner --suppress-usage \ --generate-modifier --generate-description --option-prefix "" diff --git a/mod/database b/mod/database deleted file mode 100644 index 9a83752..0000000 --- a/mod/database +++ /dev/null @@ -1,27 +0,0 @@ -// file : mod/database -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_DATABASE -#define MOD_DATABASE - -#include // database - -#include -#include - -namespace brep -{ - // Return pointer to the shared database instance, creating one on the first - // call. Throw odb::exception on failure. Is not thread-safe. - // - shared_ptr - shared_database (string user, - string password, - string name, - string host, - uint16_t port, - size_t max_connections); -} - -#endif // MOD_DATABASE diff --git a/mod/database-module b/mod/database-module deleted file mode 100644 index 3799e7b..0000000 --- a/mod/database-module +++ /dev/null @@ -1,73 +0,0 @@ -// file : mod/database-module -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_DATABASE_MODULE -#define MOD_DATABASE_MODULE - -#include // database - -#include -#include - -#include - -#include -#include - -namespace brep -{ - // A module that utilises the database. Specifically, it will retry the - // request in the face of recoverable database failures (deadlock, loss of - // connection, etc) up to a certain number of times. - // - class database_module: public module - { - protected: - database_module () = default; - - // Create a shallow copy (handling instance) if initialized and a deep - // copy (context exemplar) otherwise. - // - explicit - database_module (const database_module&); - - // Required to avoid getting warning from clang that - // database_module::init() hides module::init() virtual functions. This - // way all functions get to the same scope and become overloaded set. - // - using module::init; - - // Initialize the package database instance. Throw odb::exception on - // failure. - // - void - init (const options::package_db&, size_t retry); - - // Initialize the build database instance and parse build configuration - // file. Throw odb::exception on database failure, tab_parsing on parsing - // error, system_error on the underlying OS error. - // - void - init (const options::build&, const options::build_db&, size_t retry); - - virtual bool - handle (request&, response&) = 0; - - protected: - size_t retry_ = 0; // Max of all retries. - - shared_ptr package_db_; - - // These are NULL if not building. - // - shared_ptr build_db_; - shared_ptr build_conf_; - - private: - virtual bool - handle (request&, response&, log&); - }; -} - -#endif // MOD_DATABASE_MODULE diff --git a/mod/database-module.cxx b/mod/database-module.cxx index e7a6883..3b5e1e1 100644 --- a/mod/database-module.cxx +++ b/mod/database-module.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include // EIO @@ -12,9 +12,9 @@ #include // throw_generic_error() -#include -#include -#include +#include +#include +#include namespace brep { diff --git a/mod/database-module.hxx b/mod/database-module.hxx new file mode 100644 index 0000000..13fd529 --- /dev/null +++ b/mod/database-module.hxx @@ -0,0 +1,73 @@ +// file : mod/database-module.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_DATABASE_MODULE_HXX +#define MOD_DATABASE_MODULE_HXX + +#include // database + +#include +#include + +#include + +#include +#include + +namespace brep +{ + // A module that utilises the database. Specifically, it will retry the + // request in the face of recoverable database failures (deadlock, loss of + // connection, etc) up to a certain number of times. + // + class database_module: public module + { + protected: + database_module () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + database_module (const database_module&); + + // Required to avoid getting warning from clang that + // database_module::init() hides module::init() virtual functions. This + // way all functions get to the same scope and become overloaded set. + // + using module::init; + + // Initialize the package database instance. Throw odb::exception on + // failure. + // + void + init (const options::package_db&, size_t retry); + + // Initialize the build database instance and parse build configuration + // file. Throw odb::exception on database failure, tab_parsing on parsing + // error, system_error on the underlying OS error. + // + void + init (const options::build&, const options::build_db&, size_t retry); + + virtual bool + handle (request&, response&) = 0; + + protected: + size_t retry_ = 0; // Max of all retries. + + shared_ptr package_db_; + + // These are NULL if not building. + // + shared_ptr build_db_; + shared_ptr build_conf_; + + private: + virtual bool + handle (request&, response&, log&); + }; +} + +#endif // MOD_DATABASE_MODULE_HXX diff --git a/mod/database.cxx b/mod/database.cxx index 22a0563..67a862b 100644 --- a/mod/database.cxx +++ b/mod/database.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include diff --git a/mod/database.hxx b/mod/database.hxx new file mode 100644 index 0000000..623e65b --- /dev/null +++ b/mod/database.hxx @@ -0,0 +1,27 @@ +// file : mod/database.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_DATABASE_HXX +#define MOD_DATABASE_HXX + +#include // database + +#include +#include + +namespace brep +{ + // Return pointer to the shared database instance, creating one on the first + // call. Throw odb::exception on failure. Is not thread-safe. + // + shared_ptr + shared_database (string user, + string password, + string name, + string host, + uint16_t port, + size_t max_connections); +} + +#endif // MOD_DATABASE_HXX diff --git a/mod/diagnostics b/mod/diagnostics deleted file mode 100644 index 496f488..0000000 --- a/mod/diagnostics +++ /dev/null @@ -1,306 +0,0 @@ -// file : mod/diagnostics -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_DIAGNOSTICS -#define MOD_DIAGNOSTICS - -#include - -#include -#include - -namespace brep -{ - struct location - { - location (): line (0), column (0) {} - location (string f, uint64_t l, uint64_t c) - : file (move (f)), line (l), column (c) {} - - string file; - uint64_t line; - uint64_t column; - }; - - enum class severity {error, warning, info, trace}; - - struct diag_entry - { - severity sev; - const char* name {nullptr}; // E.g., a function name in tracing. - location loc; - string msg; - }; - - using diag_data = vector; - - // - // - template struct diag_prologue; - template struct diag_mark; - - using diag_epilogue = function; - - struct diag_record - { - template - friend const diag_record& - operator<< (const diag_record& r, const T& x) - { - r.os_ << x; - return r; - } - - diag_record () = default; - - template - explicit - diag_record (const diag_prologue& p) {*this << p;} // See below. - - template - explicit - diag_record (const diag_mark& m) {*this << m;} // See below. - - ~diag_record () noexcept(false); - - void - append (const diag_epilogue& e) const - { - if (epilogue_ == nullptr) // Keep the first epilogue (think 'fail'). - epilogue_ = &e; - - if (!data_.empty ()) - { - data_.back ().msg = os_.str (); - - // Reset the stream. There got to be a more efficient way to do it. - // - os_.clear (); - os_.str (""); - } - - data_.push_back (diag_entry ()); - } - - diag_entry& - current () const {return data_.back ();} - - // Move constructible-only type. - // - /* - @@ libstdc++ doesn't yet have the ostringstream move support. - - diag_record (diag_record&& r) - : data_ (move (r.data_)), os_ (move (r.os_)) - { - epilogue_ = r.epilogue_; - r.data_.clear (); // Empty. - } - */ - - diag_record (diag_record&& r): data_ (move (r.data_)) - { - if (!data_.empty ()) - os_ << r.os_.str (); - - epilogue_ = r.epilogue_; - r.data_.clear (); // Empty. - } - - diag_record& operator= (diag_record&&) = delete; - - diag_record (const diag_record&) = delete; - diag_record& operator= (const diag_record&) = delete; - - private: - mutable diag_data data_; - mutable std::ostringstream os_; - mutable const diag_epilogue* epilogue_ {nullptr}; - }; - - // Base (B) should provide operator() that configures diag_record. - // - template - struct diag_prologue: B - { - diag_prologue (const diag_epilogue& e): B (), epilogue_ (e) {} - - template - diag_prologue (const diag_epilogue& e, A&&... a) - : B (forward (a)...), epilogue_ (e) {} - - template - diag_record - operator<< (const T& x) const - { - diag_record r; - r.append (epilogue_); - B::operator() (r); - r << x; - return r; - } - - friend const diag_record& - operator<< (const diag_record& r, const diag_prologue& p) - { - r.append (p.epilogue_); - p (r); - return r; - } - - private: - const diag_epilogue& epilogue_; - }; - - // Base (B) should provide operator() that returns diag_prologue. - // - template - struct diag_mark: B - { - diag_mark (): B () {} - - template - diag_mark (A&&... a): B (forward (a)...) {} - - template - diag_record - operator<< (const T& x) const - { - return B::operator() () << x; - } - - friend const diag_record& - operator<< (const diag_record& r, const diag_mark& m) - { - return r << m (); - } - }; - - // Prologues. - // - struct simple_prologue_base - { - explicit - simple_prologue_base (severity s, const char* name) - : sev_ (s), name_ (name) {} - - void - operator() (const diag_record& r) const - { - diag_entry& e (r.current ()); - e.sev = sev_; - e.name = name_; - } - - private: - severity sev_; - const char* name_; - }; - typedef diag_prologue simple_prologue; - - struct location_prologue_base - { - location_prologue_base (severity s, - const char* name, - const location& l) - : sev_ (s), name_ (name), loc_ (l) {} - - void - operator() (const diag_record& r) const - { - diag_entry& e (r.current ()); - e.sev = sev_; - e.name = name_; - e.loc = loc_; //@@ I think we can probably move it. - } - - private: - severity sev_; - const char* name_; - const location loc_; - }; - typedef diag_prologue location_prologue; - - // Marks. - // - struct basic_mark_base - { - explicit - basic_mark_base (severity s, - const diag_epilogue& e, - const char* name = nullptr, - const void* data = nullptr) - : sev_ (s), epilogue_ (e), name_ (name), data_ (data) {} - - simple_prologue - operator() () const - { - return simple_prologue (epilogue_, sev_, name_); - } - - location_prologue - operator() (const location& l) const - { - return location_prologue (epilogue_, sev_, name_, l); - } - - template - location_prologue - operator() (const L& l) const - { - // get_location() is the user-supplied ADL-searched function. - // - return location_prologue ( - epilogue_, sev_, name_, get_location (l, data_)); - } - - private: - severity sev_; - const diag_epilogue& epilogue_; - const char* name_; - const void* data_; - }; - typedef diag_mark basic_mark; - - template - struct fail_mark_base - { - explicit - fail_mark_base (const char* name = nullptr, const void* data = nullptr) - : name_ (name), data_ (data) {} - - simple_prologue - operator() () const - { - return simple_prologue (epilogue_, severity::error, name_); - } - - location_prologue - operator() (const location& l) const - { - return location_prologue (epilogue_, severity::error, name_, l); - } - - template - location_prologue - operator() (const L& l) const - { - return location_prologue ( - epilogue_, severity::error, name_, get_location (l, data_)); - } - - static void - epilogue (diag_data&& d) {throw E (move (d));} - - private: - const diag_epilogue epilogue_ {&epilogue}; - const char* name_; - const void* data_; - }; - - template - using fail_mark = diag_mark>; -} - -#endif // MOD_DIAGNOSTICS diff --git a/mod/diagnostics.cxx b/mod/diagnostics.cxx index 066c4b2..c2f3f52 100644 --- a/mod/diagnostics.cxx +++ b/mod/diagnostics.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include using namespace std; diff --git a/mod/diagnostics.hxx b/mod/diagnostics.hxx new file mode 100644 index 0000000..e05d56a --- /dev/null +++ b/mod/diagnostics.hxx @@ -0,0 +1,306 @@ +// file : mod/diagnostics.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_DIAGNOSTICS_HXX +#define MOD_DIAGNOSTICS_HXX + +#include + +#include +#include + +namespace brep +{ + struct location + { + location (): line (0), column (0) {} + location (string f, uint64_t l, uint64_t c) + : file (move (f)), line (l), column (c) {} + + string file; + uint64_t line; + uint64_t column; + }; + + enum class severity {error, warning, info, trace}; + + struct diag_entry + { + severity sev; + const char* name {nullptr}; // E.g., a function name in tracing. + location loc; + string msg; + }; + + using diag_data = vector; + + // + // + template struct diag_prologue; + template struct diag_mark; + + using diag_epilogue = function; + + struct diag_record + { + template + friend const diag_record& + operator<< (const diag_record& r, const T& x) + { + r.os_ << x; + return r; + } + + diag_record () = default; + + template + explicit + diag_record (const diag_prologue& p) {*this << p;} // See below. + + template + explicit + diag_record (const diag_mark& m) {*this << m;} // See below. + + ~diag_record () noexcept(false); + + void + append (const diag_epilogue& e) const + { + if (epilogue_ == nullptr) // Keep the first epilogue (think 'fail'). + epilogue_ = &e; + + if (!data_.empty ()) + { + data_.back ().msg = os_.str (); + + // Reset the stream. There got to be a more efficient way to do it. + // + os_.clear (); + os_.str (""); + } + + data_.push_back (diag_entry ()); + } + + diag_entry& + current () const {return data_.back ();} + + // Move constructible-only type. + // + /* + @@ libstdc++ doesn't yet have the ostringstream move support. + + diag_record (diag_record&& r) + : data_ (move (r.data_)), os_ (move (r.os_)) + { + epilogue_ = r.epilogue_; + r.data_.clear (); // Empty. + } + */ + + diag_record (diag_record&& r): data_ (move (r.data_)) + { + if (!data_.empty ()) + os_ << r.os_.str (); + + epilogue_ = r.epilogue_; + r.data_.clear (); // Empty. + } + + diag_record& operator= (diag_record&&) = delete; + + diag_record (const diag_record&) = delete; + diag_record& operator= (const diag_record&) = delete; + + private: + mutable diag_data data_; + mutable std::ostringstream os_; + mutable const diag_epilogue* epilogue_ {nullptr}; + }; + + // Base (B) should provide operator() that configures diag_record. + // + template + struct diag_prologue: B + { + diag_prologue (const diag_epilogue& e): B (), epilogue_ (e) {} + + template + diag_prologue (const diag_epilogue& e, A&&... a) + : B (forward (a)...), epilogue_ (e) {} + + template + diag_record + operator<< (const T& x) const + { + diag_record r; + r.append (epilogue_); + B::operator() (r); + r << x; + return r; + } + + friend const diag_record& + operator<< (const diag_record& r, const diag_prologue& p) + { + r.append (p.epilogue_); + p (r); + return r; + } + + private: + const diag_epilogue& epilogue_; + }; + + // Base (B) should provide operator() that returns diag_prologue. + // + template + struct diag_mark: B + { + diag_mark (): B () {} + + template + diag_mark (A&&... a): B (forward (a)...) {} + + template + diag_record + operator<< (const T& x) const + { + return B::operator() () << x; + } + + friend const diag_record& + operator<< (const diag_record& r, const diag_mark& m) + { + return r << m (); + } + }; + + // Prologues. + // + struct simple_prologue_base + { + explicit + simple_prologue_base (severity s, const char* name) + : sev_ (s), name_ (name) {} + + void + operator() (const diag_record& r) const + { + diag_entry& e (r.current ()); + e.sev = sev_; + e.name = name_; + } + + private: + severity sev_; + const char* name_; + }; + typedef diag_prologue simple_prologue; + + struct location_prologue_base + { + location_prologue_base (severity s, + const char* name, + const location& l) + : sev_ (s), name_ (name), loc_ (l) {} + + void + operator() (const diag_record& r) const + { + diag_entry& e (r.current ()); + e.sev = sev_; + e.name = name_; + e.loc = loc_; //@@ I think we can probably move it. + } + + private: + severity sev_; + const char* name_; + const location loc_; + }; + typedef diag_prologue location_prologue; + + // Marks. + // + struct basic_mark_base + { + explicit + basic_mark_base (severity s, + const diag_epilogue& e, + const char* name = nullptr, + const void* data = nullptr) + : sev_ (s), epilogue_ (e), name_ (name), data_ (data) {} + + simple_prologue + operator() () const + { + return simple_prologue (epilogue_, sev_, name_); + } + + location_prologue + operator() (const location& l) const + { + return location_prologue (epilogue_, sev_, name_, l); + } + + template + location_prologue + operator() (const L& l) const + { + // get_location() is the user-supplied ADL-searched function. + // + return location_prologue ( + epilogue_, sev_, name_, get_location (l, data_)); + } + + private: + severity sev_; + const diag_epilogue& epilogue_; + const char* name_; + const void* data_; + }; + typedef diag_mark basic_mark; + + template + struct fail_mark_base + { + explicit + fail_mark_base (const char* name = nullptr, const void* data = nullptr) + : name_ (name), data_ (data) {} + + simple_prologue + operator() () const + { + return simple_prologue (epilogue_, severity::error, name_); + } + + location_prologue + operator() (const location& l) const + { + return location_prologue (epilogue_, severity::error, name_, l); + } + + template + location_prologue + operator() (const L& l) const + { + return location_prologue ( + epilogue_, severity::error, name_, get_location (l, data_)); + } + + static void + epilogue (diag_data&& d) {throw E (move (d));} + + private: + const diag_epilogue epilogue_ {&epilogue}; + const char* name_; + const void* data_; + }; + + template + using fail_mark = diag_mark>; +} + +#endif // MOD_DIAGNOSTICS_HXX diff --git a/mod/mod-build-force b/mod/mod-build-force deleted file mode 100644 index 3fa723d..0000000 --- a/mod/mod-build-force +++ /dev/null @@ -1,45 +0,0 @@ -// file : mod/mod-build-force -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_MOD_BUILD_FORCE -#define MOD_MOD_BUILD_FORCE - -#include -#include - -#include -#include - -namespace brep -{ - class build_force: public database_module - { - public: - build_force () = default; - - // Create a shallow copy (handling instance) if initialized and a deep - // copy (context exemplar) otherwise. - // - explicit - build_force (const build_force&); - - virtual bool - handle (request&, response&); - - virtual const cli::options& - cli_options () const - { - return options::build_force::description (); - } - - private: - virtual void - init (cli::scanner&); - - private: - shared_ptr options_; - }; -} - -#endif // MOD_MOD_BUILD_FORCE diff --git a/mod/mod-build-force.cxx b/mod/mod-build-force.cxx index 1db25d1..e0c641e 100644 --- a/mod/mod-build-force.cxx +++ b/mod/mod-build-force.cxx @@ -2,21 +2,21 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include // replace() #include #include -#include +#include -#include -#include -#include -#include +#include +#include +#include +#include -#include +#include using namespace std; using namespace bbot; diff --git a/mod/mod-build-force.hxx b/mod/mod-build-force.hxx new file mode 100644 index 0000000..02b6ea4 --- /dev/null +++ b/mod/mod-build-force.hxx @@ -0,0 +1,45 @@ +// file : mod/mod-build-force.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_MOD_BUILD_FORCE_HXX +#define MOD_MOD_BUILD_FORCE_HXX + +#include +#include + +#include +#include + +namespace brep +{ + class build_force: public database_module + { + public: + build_force () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + build_force (const build_force&); + + virtual bool + handle (request&, response&); + + virtual const cli::options& + cli_options () const + { + return options::build_force::description (); + } + + private: + virtual void + init (cli::scanner&); + + private: + shared_ptr options_; + }; +} + +#endif // MOD_MOD_BUILD_FORCE_HXX diff --git a/mod/mod-build-log b/mod/mod-build-log deleted file mode 100644 index 8395546..0000000 --- a/mod/mod-build-log +++ /dev/null @@ -1,45 +0,0 @@ -// file : mod/mod-build-log -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_MOD_BUILD_LOG -#define MOD_MOD_BUILD_LOG - -#include -#include - -#include -#include - -namespace brep -{ - class build_log: public database_module - { - public: - build_log () = default; - - // Create a shallow copy (handling instance) if initialized and a deep - // copy (context exemplar) otherwise. - // - explicit - build_log (const build_log&); - - virtual bool - handle (request&, response&); - - virtual const cli::options& - cli_options () const - { - return options::build_log::description (); - } - - private: - virtual void - init (cli::scanner&); - - private: - shared_ptr options_; - }; -} - -#endif // MOD_MOD_BUILD_LOG diff --git a/mod/mod-build-log.cxx b/mod/mod-build-log.cxx index 3dddd82..09591b9 100644 --- a/mod/mod-build-log.cxx +++ b/mod/mod-build-log.cxx @@ -2,21 +2,21 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include // find_if() #include #include -#include +#include -#include -#include -#include -#include +#include +#include +#include +#include -#include +#include using namespace std; using namespace bbot; diff --git a/mod/mod-build-log.hxx b/mod/mod-build-log.hxx new file mode 100644 index 0000000..76e99c7 --- /dev/null +++ b/mod/mod-build-log.hxx @@ -0,0 +1,45 @@ +// file : mod/mod-build-log.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_MOD_BUILD_LOG_HXX +#define MOD_MOD_BUILD_LOG_HXX + +#include +#include + +#include +#include + +namespace brep +{ + class build_log: public database_module + { + public: + build_log () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + build_log (const build_log&); + + virtual bool + handle (request&, response&); + + virtual const cli::options& + cli_options () const + { + return options::build_log::description (); + } + + private: + virtual void + init (cli::scanner&); + + private: + shared_ptr options_; + }; +} + +#endif // MOD_MOD_BUILD_LOG_HXX diff --git a/mod/mod-build-result b/mod/mod-build-result deleted file mode 100644 index 8afa697..0000000 --- a/mod/mod-build-result +++ /dev/null @@ -1,42 +0,0 @@ -// file : mod/mod-build-result -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_MOD_BUILD_RESULT -#define MOD_MOD_BUILD_RESULT - -#include -#include - -#include -#include - -namespace brep -{ - class build_result: public database_module - { - public: - build_result () = default; - - // Create a shallow copy (handling instance) if initialized and a deep - // copy (context exemplar) otherwise. - // - explicit - build_result (const build_result&); - - virtual bool - handle (request&, response&); - - virtual const cli::options& - cli_options () const {return options::build_result::description ();} - - private: - virtual void - init (cli::scanner&); - - private: - shared_ptr options_; - }; -} - -#endif // MOD_MOD_BUILD_RESULT diff --git a/mod/mod-build-result.cxx b/mod/mod-build-result.cxx index 23381de..d6ec5e9 100644 --- a/mod/mod-build-result.cxx +++ b/mod/mod-build-result.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include // find_if() @@ -11,20 +11,20 @@ #include #include -#include +#include #include #include -#include -#include +#include +#include -#include -#include -#include -#include +#include +#include +#include +#include -#include +#include using namespace std; using namespace butl; diff --git a/mod/mod-build-result.hxx b/mod/mod-build-result.hxx new file mode 100644 index 0000000..4af9b1b --- /dev/null +++ b/mod/mod-build-result.hxx @@ -0,0 +1,42 @@ +// file : mod/mod-build-result.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_MOD_BUILD_RESULT_HXX +#define MOD_MOD_BUILD_RESULT_HXX + +#include +#include + +#include +#include + +namespace brep +{ + class build_result: public database_module + { + public: + build_result () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + build_result (const build_result&); + + virtual bool + handle (request&, response&); + + virtual const cli::options& + cli_options () const {return options::build_result::description ();} + + private: + virtual void + init (cli::scanner&); + + private: + shared_ptr options_; + }; +} + +#endif // MOD_MOD_BUILD_RESULT_HXX diff --git a/mod/mod-build-task b/mod/mod-build-task deleted file mode 100644 index 051357e..0000000 --- a/mod/mod-build-task +++ /dev/null @@ -1,42 +0,0 @@ -// file : mod/mod-build-task -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_MOD_BUILD_TASK -#define MOD_MOD_BUILD_TASK - -#include -#include - -#include -#include - -namespace brep -{ - class build_task: public database_module - { - public: - build_task () = default; - - // Create a shallow copy (handling instance) if initialized and a deep - // copy (context exemplar) otherwise. - // - explicit - build_task (const build_task&); - - virtual bool - handle (request&, response&); - - virtual const cli::options& - cli_options () const {return options::build_task::description ();} - - private: - virtual void - init (cli::scanner&); - - private: - shared_ptr options_; - }; -} - -#endif // MOD_MOD_BUILD_TASK diff --git a/mod/mod-build-task.cxx b/mod/mod-build-task.cxx index 9a72f66..353e636 100644 --- a/mod/mod-build-task.cxx +++ b/mod/mod-build-task.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include #include @@ -12,20 +12,20 @@ #include #include -#include -#include +#include +#include #include #include -#include +#include -#include -#include -#include -#include +#include +#include +#include +#include -#include +#include using namespace std; using namespace butl; diff --git a/mod/mod-build-task.hxx b/mod/mod-build-task.hxx new file mode 100644 index 0000000..d69836f --- /dev/null +++ b/mod/mod-build-task.hxx @@ -0,0 +1,42 @@ +// file : mod/mod-build-task.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_MOD_BUILD_TASK_HXX +#define MOD_MOD_BUILD_TASK_HXX + +#include +#include + +#include +#include + +namespace brep +{ + class build_task: public database_module + { + public: + build_task () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + build_task (const build_task&); + + virtual bool + handle (request&, response&); + + virtual const cli::options& + cli_options () const {return options::build_task::description ();} + + private: + virtual void + init (cli::scanner&); + + private: + shared_ptr options_; + }; +} + +#endif // MOD_MOD_BUILD_TASK_HXX diff --git a/mod/mod-package-details b/mod/mod-package-details deleted file mode 100644 index a19e0d7..0000000 --- a/mod/mod-package-details +++ /dev/null @@ -1,42 +0,0 @@ -// file : mod/mod-package-details -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_MOD_PACKAGE_DETAILS -#define MOD_MOD_PACKAGE_DETAILS - -#include -#include - -#include -#include - -namespace brep -{ - class package_details: public database_module - { - public: - package_details () = default; - - // Create a shallow copy (handling instance) if initialized and a deep - // copy (context exemplar) otherwise. - // - explicit - package_details (const package_details&); - - virtual bool - handle (request&, response&); - - virtual const cli::options& - cli_options () const {return options::package_details::description ();} - - private: - virtual void - init (cli::scanner&); - - private: - shared_ptr options_; - }; -} - -#endif // MOD_MOD_PACKAGE_DETAILS diff --git a/mod/mod-package-details.cxx b/mod/mod-package-details.cxx index fd3fd6d..9a9bfcc 100644 --- a/mod/mod-package-details.cxx +++ b/mod/mod-package-details.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include @@ -10,15 +10,15 @@ #include #include -#include -#include -#include +#include +#include +#include -#include -#include +#include +#include -#include -#include +#include +#include using namespace odb::core; using namespace brep::cli; diff --git a/mod/mod-package-details.hxx b/mod/mod-package-details.hxx new file mode 100644 index 0000000..e3950aa --- /dev/null +++ b/mod/mod-package-details.hxx @@ -0,0 +1,42 @@ +// file : mod/mod-package-details.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_MOD_PACKAGE_DETAILS_HXX +#define MOD_MOD_PACKAGE_DETAILS_HXX + +#include +#include + +#include +#include + +namespace brep +{ + class package_details: public database_module + { + public: + package_details () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + package_details (const package_details&); + + virtual bool + handle (request&, response&); + + virtual const cli::options& + cli_options () const {return options::package_details::description ();} + + private: + virtual void + init (cli::scanner&); + + private: + shared_ptr options_; + }; +} + +#endif // MOD_MOD_PACKAGE_DETAILS_HXX diff --git a/mod/mod-package-search b/mod/mod-package-search deleted file mode 100644 index d8d09be..0000000 --- a/mod/mod-package-search +++ /dev/null @@ -1,42 +0,0 @@ -// file : mod/mod-package-search -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_MOD_PACKAGE_SEARCH -#define MOD_MOD_PACKAGE_SEARCH - -#include -#include - -#include -#include - -namespace brep -{ - class package_search: public database_module - { - public: - package_search () = default; - - // Create a shallow copy (handling instance) if initialized and a deep - // copy (context exemplar) otherwise. - // - explicit - package_search (const package_search&); - - virtual bool - handle (request&, response&); - - virtual const cli::options& - cli_options () const {return options::package_search::description ();} - - private: - virtual void - init (cli::scanner&); - - private: - shared_ptr options_; - }; -} - -#endif // MOD_MOD_PACKAGE_SEARCH diff --git a/mod/mod-package-search.cxx b/mod/mod-package-search.cxx index 6d73a69..8897792 100644 --- a/mod/mod-package-search.cxx +++ b/mod/mod-package-search.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include @@ -11,15 +11,15 @@ #include #include -#include -#include -#include +#include +#include +#include -#include -#include +#include +#include -#include -#include +#include +#include using namespace odb::core; using namespace brep::cli; diff --git a/mod/mod-package-search.hxx b/mod/mod-package-search.hxx new file mode 100644 index 0000000..5e7385e --- /dev/null +++ b/mod/mod-package-search.hxx @@ -0,0 +1,42 @@ +// file : mod/mod-package-search.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_MOD_PACKAGE_SEARCH_HXX +#define MOD_MOD_PACKAGE_SEARCH_HXX + +#include +#include + +#include +#include + +namespace brep +{ + class package_search: public database_module + { + public: + package_search () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + package_search (const package_search&); + + virtual bool + handle (request&, response&); + + virtual const cli::options& + cli_options () const {return options::package_search::description ();} + + private: + virtual void + init (cli::scanner&); + + private: + shared_ptr options_; + }; +} + +#endif // MOD_MOD_PACKAGE_SEARCH_HXX diff --git a/mod/mod-package-version-details b/mod/mod-package-version-details deleted file mode 100644 index 70ec82b..0000000 --- a/mod/mod-package-version-details +++ /dev/null @@ -1,45 +0,0 @@ -// file : mod/mod-package-version-details -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_MOD_PACKAGE_VERSION_DETAILS -#define MOD_MOD_PACKAGE_VERSION_DETAILS - -#include -#include - -#include -#include - -namespace brep -{ - class package_version_details: public database_module - { - public: - package_version_details () = default; - - // Create a shallow copy (handling instance) if initialized and a deep - // copy (context exemplar) otherwise. - // - explicit - package_version_details (const package_version_details&); - - virtual bool - handle (request&, response&); - - virtual const cli::options& - cli_options () const - { - return options::package_version_details::description (); - } - - private: - virtual void - init (cli::scanner&); - - private: - shared_ptr options_; - }; -} - -#endif // MOD_MOD_PACKAGE_VERSION_DETAILS diff --git a/mod/mod-package-version-details.cxx b/mod/mod-package-version-details.cxx index 30b2567..34c7f3b 100644 --- a/mod/mod-package-version-details.cxx +++ b/mod/mod-package-version-details.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include @@ -10,15 +10,15 @@ #include #include -#include -#include -#include +#include +#include +#include -#include -#include +#include +#include -#include -#include +#include +#include using namespace std; using namespace odb::core; diff --git a/mod/mod-package-version-details.hxx b/mod/mod-package-version-details.hxx new file mode 100644 index 0000000..ef682d7 --- /dev/null +++ b/mod/mod-package-version-details.hxx @@ -0,0 +1,45 @@ +// file : mod/mod-package-version-details.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_MOD_PACKAGE_VERSION_DETAILS_HXX +#define MOD_MOD_PACKAGE_VERSION_DETAILS_HXX + +#include +#include + +#include +#include + +namespace brep +{ + class package_version_details: public database_module + { + public: + package_version_details () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + package_version_details (const package_version_details&); + + virtual bool + handle (request&, response&); + + virtual const cli::options& + cli_options () const + { + return options::package_version_details::description (); + } + + private: + virtual void + init (cli::scanner&); + + private: + shared_ptr options_; + }; +} + +#endif // MOD_MOD_PACKAGE_VERSION_DETAILS_HXX diff --git a/mod/mod-repository-details b/mod/mod-repository-details deleted file mode 100644 index 5e797b2..0000000 --- a/mod/mod-repository-details +++ /dev/null @@ -1,42 +0,0 @@ -// file : mod/mod-repository-details -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_MOD_REPOSITORY_DETAILS -#define MOD_MOD_REPOSITORY_DETAILS - -#include -#include - -#include -#include - -namespace brep -{ - class repository_details: public database_module - { - public: - repository_details () = default; - - // Create a shallow copy (handling instance) if initialized and a deep - // copy (context exemplar) otherwise. - // - explicit - repository_details (const repository_details&); - - virtual bool - handle (request&, response&); - - virtual const cli::options& - cli_options () const {return options::repository_details::description ();} - - private: - virtual void - init (cli::scanner&); - - private: - shared_ptr options_; - }; -} - -#endif // MOD_MOD_REPOSITORY_DETAILS diff --git a/mod/mod-repository-details.cxx b/mod/mod-repository-details.cxx index 9e791ef..78b309d 100644 --- a/mod/mod-repository-details.cxx +++ b/mod/mod-repository-details.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include // tzset() @@ -16,15 +16,15 @@ #include -#include -#include -#include +#include +#include +#include -#include -#include +#include +#include -#include -#include +#include +#include using namespace std; using namespace odb::core; diff --git a/mod/mod-repository-details.hxx b/mod/mod-repository-details.hxx new file mode 100644 index 0000000..8a795dc --- /dev/null +++ b/mod/mod-repository-details.hxx @@ -0,0 +1,42 @@ +// file : mod/mod-repository-details.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_MOD_REPOSITORY_DETAILS_HXX +#define MOD_MOD_REPOSITORY_DETAILS_HXX + +#include +#include + +#include +#include + +namespace brep +{ + class repository_details: public database_module + { + public: + repository_details () = default; + + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + repository_details (const repository_details&); + + virtual bool + handle (request&, response&); + + virtual const cli::options& + cli_options () const {return options::repository_details::description ();} + + private: + virtual void + init (cli::scanner&); + + private: + shared_ptr options_; + }; +} + +#endif // MOD_MOD_REPOSITORY_DETAILS_HXX diff --git a/mod/mod-repository-root b/mod/mod-repository-root deleted file mode 100644 index b347cd3..0000000 --- a/mod/mod-repository-root +++ /dev/null @@ -1,77 +0,0 @@ -// file : mod/mod-repository-root -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_MOD_REPOSITORY_ROOT -#define MOD_MOD_REPOSITORY_ROOT - -#include -#include - -#include -#include - -namespace brep -{ - class package_search; - class package_details; - class package_version_details; - class repository_details; - class build_task; - class build_result; - class build_force; - class build_log; - - class repository_root: public module - { - public: - repository_root (); - - // Copy constructible-only type. - // - // Create a shallow copy (handling instance) if initialized and a deep - // copy (context exemplar) otherwise. - // - explicit - repository_root (const repository_root&); - - private: - virtual bool - handle (request&, response&); - - virtual const cli::options& - cli_options () const {return options::repository_root::description ();} - - virtual option_descriptions - options (); - - virtual void - init (const name_values&); - - virtual void - init (cli::scanner&); - - virtual void - version (); - - private: - shared_ptr package_search_; - shared_ptr package_details_; - shared_ptr package_version_details_; - shared_ptr repository_details_; - shared_ptr build_task_; - shared_ptr build_result_; - shared_ptr build_force_; - shared_ptr build_log_; - shared_ptr options_; - - // Sub-module the request is dispatched to. Initially is NULL. It is set - // by the first call to handle() to a deep copy of the selected exemplar. - // The subsequent calls of handle() (that may take place after the retry - // exception is thrown) will use the existing handler instance. - // - unique_ptr handler_; - }; -} - -#endif // MOD_MOD_REPOSITORY_ROOT diff --git a/mod/mod-repository-root.cxx b/mod/mod-repository-root.cxx index 097c874..bac63ca 100644 --- a/mod/mod-repository-root.cxx +++ b/mod/mod-repository-root.cxx @@ -2,22 +2,22 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace std; using namespace brep::cli; diff --git a/mod/mod-repository-root.hxx b/mod/mod-repository-root.hxx new file mode 100644 index 0000000..f54fa62 --- /dev/null +++ b/mod/mod-repository-root.hxx @@ -0,0 +1,77 @@ +// file : mod/mod-repository-root.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_MOD_REPOSITORY_ROOT_HXX +#define MOD_MOD_REPOSITORY_ROOT_HXX + +#include +#include + +#include +#include + +namespace brep +{ + class package_search; + class package_details; + class package_version_details; + class repository_details; + class build_task; + class build_result; + class build_force; + class build_log; + + class repository_root: public module + { + public: + repository_root (); + + // Copy constructible-only type. + // + // Create a shallow copy (handling instance) if initialized and a deep + // copy (context exemplar) otherwise. + // + explicit + repository_root (const repository_root&); + + private: + virtual bool + handle (request&, response&); + + virtual const cli::options& + cli_options () const {return options::repository_root::description ();} + + virtual option_descriptions + options (); + + virtual void + init (const name_values&); + + virtual void + init (cli::scanner&); + + virtual void + version (); + + private: + shared_ptr package_search_; + shared_ptr package_details_; + shared_ptr package_version_details_; + shared_ptr repository_details_; + shared_ptr build_task_; + shared_ptr build_result_; + shared_ptr build_force_; + shared_ptr build_log_; + shared_ptr options_; + + // Sub-module the request is dispatched to. Initially is NULL. It is set + // by the first call to handle() to a deep copy of the selected exemplar. + // The subsequent calls of handle() (that may take place after the retry + // exception is thrown) will use the existing handler instance. + // + unique_ptr handler_; + }; +} + +#endif // MOD_MOD_REPOSITORY_ROOT_HXX diff --git a/mod/module b/mod/module deleted file mode 100644 index f0743bb..0000000 --- a/mod/module +++ /dev/null @@ -1,201 +0,0 @@ -// file : mod/module -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_MODULE -#define MOD_MODULE - -#include - -#include -#include - -#include -#include - -namespace brep -{ - // Bring in commonly used names from the web namespace. - // - // @@ Maybe doing using namespace is the right way to handle this. - // There will, however, most likely be a conflict between - // web::module and our module. Or maybe not, need to try. - // - using web::status_code; - using web::invalid_request; - using web::sequence_error; - using web::option_descriptions; - using web::name_value; - using web::name_values; - using web::request; - using web::response; - using web::log; - - // This exception indicated a server error (5XX). In particular, - // it is thrown by the fail diagnostics stream and is caught by the - // module implementation where it is both logged as an error and - // returned to the user with the 5XX status code. - // - struct server_error - { - diag_data data; - - server_error (diag_data&& d): data (move (d)) {} - }; - - // Every module member function that needs to produce any diagnostics - // shall begin with: - // - // MODULE_DIAG; - // - // This will instantiate the fail, error, warn, info, and trace - // diagnostics streams with the function's name. - // -#define MODULE_DIAG \ - const fail_mark fail (__PRETTY_FUNCTION__); \ - const basic_mark error (severity::error, \ - this->log_writer_, \ - __PRETTY_FUNCTION__); \ - const basic_mark warn (severity::warning, \ - this->log_writer_, \ - __PRETTY_FUNCTION__); \ - const basic_mark info (severity::info, \ - this->log_writer_, \ - __PRETTY_FUNCTION__); \ - const basic_mark trace (severity::trace, \ - this->log_writer_, \ - __PRETTY_FUNCTION__) - - // Adaptation of the web::module to our needs. - // - class module: public web::module - { - // Diagnostics. - // - protected: - // Trace verbosity level. - // - // 0 - tracing disabled. - // 1 - brief information regarding irregular situations, which not being - // an error can be of some interest. - // 2 - @@ TODO: document - // - // While uint8 is more than enough, use uint16 for the ease of printing. - // - uint16_t verb_ = 0; - - template void l1 (const F& f) const {if (verb_ >= 1) f ();} - template void l2 (const F& f) const {if (verb_ >= 2) f ();} - - // Set to true when the module is successfully initialized. - // - bool initialized_ {false}; - - // Implementation details. - // - protected: - module (); - module (const module& ); - - static name_values - filter (const name_values&, const option_descriptions&); - - static option_descriptions - convert (const cli::options&); - - static void - append (option_descriptions& dst, const cli::options& src); - - static void - append (option_descriptions& dst, const option_descriptions& src); - - // Can be used by module implementation to parse HTTP request parameters. - // - class name_value_scanner: public cli::scanner - { - public: - name_value_scanner (const name_values&) noexcept; - - virtual bool - more (); - - virtual const char* - peek (); - - virtual const char* - next (); - - virtual void - skip (); - - private: - const name_values& name_values_; - name_values::const_iterator i_; - bool name_; - }; - - public: - virtual const cli::options& - cli_options () const = 0; - - virtual void - init (cli::scanner&) = 0; - - // Can be overriden by custom request dispatcher to initialize - // sub-modules. - // - virtual void - init (const name_values&); - - virtual void - init (const name_values&, log&); - - virtual bool - handle (request&, response&) = 0; - - virtual bool - handle (request&, response&, log&); - - // web::module interface. - // - public: - // Custom request dispatcher can aggregate its own option descriptions - // with sub-modules option descriptions. In this case it should still call - // the base implementation in order to include the brep::module's options. - // - virtual option_descriptions - options (); - - private: - virtual void - version (log&); - - // Can be overriden by the module implementation to log version, etc. - // - virtual void - version () {} - - name_values - expand_options (const name_values&); - - // Diagnostics implementation details. - // - protected: - log* log_ {nullptr}; // Diagnostics backend provided by the web server. - - private: - // Extract function name from a __PRETTY_FUNCTION__. - // Throw invalid_argument if fail to parse. - // - static string - func_name (const char* pretty_name); - - void - log_write (const diag_data&) const; - - protected: - const diag_epilogue log_writer_; - }; -} - -#endif // MOD_MODULE diff --git a/mod/module.cxx b/mod/module.cxx index e42f937..5db1fbe 100644 --- a/mod/module.cxx +++ b/mod/module.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include #include @@ -11,10 +11,10 @@ #include // strchr() #include // bind() -#include -#include +#include +#include -#include +#include using namespace std; using namespace placeholders; // For std::bind's _1, etc. diff --git a/mod/module.hxx b/mod/module.hxx new file mode 100644 index 0000000..1cd6b8d --- /dev/null +++ b/mod/module.hxx @@ -0,0 +1,201 @@ +// file : mod/module.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_MODULE_HXX +#define MOD_MODULE_HXX + +#include + +#include +#include + +#include +#include + +namespace brep +{ + // Bring in commonly used names from the web namespace. + // + // @@ Maybe doing using namespace is the right way to handle this. + // There will, however, most likely be a conflict between + // web::module and our module. Or maybe not, need to try. + // + using web::status_code; + using web::invalid_request; + using web::sequence_error; + using web::option_descriptions; + using web::name_value; + using web::name_values; + using web::request; + using web::response; + using web::log; + + // This exception indicated a server error (5XX). In particular, + // it is thrown by the fail diagnostics stream and is caught by the + // module implementation where it is both logged as an error and + // returned to the user with the 5XX status code. + // + struct server_error + { + diag_data data; + + server_error (diag_data&& d): data (move (d)) {} + }; + + // Every module member function that needs to produce any diagnostics + // shall begin with: + // + // MODULE_DIAG; + // + // This will instantiate the fail, error, warn, info, and trace + // diagnostics streams with the function's name. + // +#define MODULE_DIAG \ + const fail_mark fail (__PRETTY_FUNCTION__); \ + const basic_mark error (severity::error, \ + this->log_writer_, \ + __PRETTY_FUNCTION__); \ + const basic_mark warn (severity::warning, \ + this->log_writer_, \ + __PRETTY_FUNCTION__); \ + const basic_mark info (severity::info, \ + this->log_writer_, \ + __PRETTY_FUNCTION__); \ + const basic_mark trace (severity::trace, \ + this->log_writer_, \ + __PRETTY_FUNCTION__) + + // Adaptation of the web::module to our needs. + // + class module: public web::module + { + // Diagnostics. + // + protected: + // Trace verbosity level. + // + // 0 - tracing disabled. + // 1 - brief information regarding irregular situations, which not being + // an error can be of some interest. + // 2 - @@ TODO: document + // + // While uint8 is more than enough, use uint16 for the ease of printing. + // + uint16_t verb_ = 0; + + template void l1 (const F& f) const {if (verb_ >= 1) f ();} + template void l2 (const F& f) const {if (verb_ >= 2) f ();} + + // Set to true when the module is successfully initialized. + // + bool initialized_ {false}; + + // Implementation details. + // + protected: + module (); + module (const module& ); + + static name_values + filter (const name_values&, const option_descriptions&); + + static option_descriptions + convert (const cli::options&); + + static void + append (option_descriptions& dst, const cli::options& src); + + static void + append (option_descriptions& dst, const option_descriptions& src); + + // Can be used by module implementation to parse HTTP request parameters. + // + class name_value_scanner: public cli::scanner + { + public: + name_value_scanner (const name_values&) noexcept; + + virtual bool + more (); + + virtual const char* + peek (); + + virtual const char* + next (); + + virtual void + skip (); + + private: + const name_values& name_values_; + name_values::const_iterator i_; + bool name_; + }; + + public: + virtual const cli::options& + cli_options () const = 0; + + virtual void + init (cli::scanner&) = 0; + + // Can be overriden by custom request dispatcher to initialize + // sub-modules. + // + virtual void + init (const name_values&); + + virtual void + init (const name_values&, log&); + + virtual bool + handle (request&, response&) = 0; + + virtual bool + handle (request&, response&, log&); + + // web::module interface. + // + public: + // Custom request dispatcher can aggregate its own option descriptions + // with sub-modules option descriptions. In this case it should still call + // the base implementation in order to include the brep::module's options. + // + virtual option_descriptions + options (); + + private: + virtual void + version (log&); + + // Can be overriden by the module implementation to log version, etc. + // + virtual void + version () {} + + name_values + expand_options (const name_values&); + + // Diagnostics implementation details. + // + protected: + log* log_ {nullptr}; // Diagnostics backend provided by the web server. + + private: + // Extract function name from a __PRETTY_FUNCTION__. + // Throw invalid_argument if fail to parse. + // + static string + func_name (const char* pretty_name); + + void + log_write (const diag_data&) const; + + protected: + const diag_epilogue log_writer_; + }; +} + +#endif // MOD_MODULE_HXX diff --git a/mod/options-types b/mod/options-types deleted file mode 100644 index d9eaf9e..0000000 --- a/mod/options-types +++ /dev/null @@ -1,31 +0,0 @@ -// file : mod/options-types -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_OPTIONS_TYPES -#define MOD_OPTIONS_TYPES - -#include -#include - -namespace brep -{ - // brep types - // - enum class page_form - { - full, - brief - }; - - struct page_menu - { - string label; - string link; - - page_menu () = default; - page_menu (string b, string l): label (move (b)), link (move (l)) {} - }; -} - -#endif // MOD_OPTIONS_TYPES diff --git a/mod/options-types.hxx b/mod/options-types.hxx new file mode 100644 index 0000000..7808ca4 --- /dev/null +++ b/mod/options-types.hxx @@ -0,0 +1,31 @@ +// file : mod/options-types.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_OPTIONS_TYPES_HXX +#define MOD_OPTIONS_TYPES_HXX + +#include +#include + +namespace brep +{ + // brep types + // + enum class page_form + { + full, + brief + }; + + struct page_menu + { + string label; + string link; + + page_menu () = default; + page_menu (string b, string l): label (move (b)), link (move (l)) {} + }; +} + +#endif // MOD_OPTIONS_TYPES_HXX diff --git a/mod/options.cli b/mod/options.cli index 0f96e9c..3d6ac60 100644 --- a/mod/options.cli +++ b/mod/options.cli @@ -2,11 +2,11 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -include ; +include ; -include ; +include ; -include ; +include ; namespace brep { diff --git a/mod/page b/mod/page deleted file mode 100644 index a7aaf1b..0000000 --- a/mod/page +++ /dev/null @@ -1,428 +0,0 @@ -// file : mod/page -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_PAGE -#define MOD_PAGE - -#include - -#include - -#include -#include - -#include - -#include // page_menu - -namespace brep -{ - // Page common building blocks. - // - - // Generates CSS link elements. - // - class CSS_LINKS - { - public: - CSS_LINKS (const path& p, const dir_path& r): path_ (p), root_ (r) {} - - void - operator() (xml::serializer&) const; - - private: - const path& path_; - const dir_path& root_; - }; - - // Generates page header element. - // - class DIV_HEADER - { - public: - DIV_HEADER (const dir_path& root, - const web::xhtml::fragment& logo, - const vector& menu): - root_ (root), logo_ (logo), menu_ (menu) {} - - void - operator() (xml::serializer&) const; - - private: - const dir_path& root_; - const web::xhtml::fragment& logo_; - const vector& menu_; - }; - - // Generates package search form element. - // - class FORM_SEARCH - { - public: - FORM_SEARCH (const string& q): query_ (q) {} - - void - operator() (xml::serializer&) const; - - private: - const string& query_; - }; - - // Generates counter element. - // - // It could be redunant to distinguish between singular and plural word forms - // if it wouldn't be so cheap in English, and phrase '1 Packages' wouldn't - // look that ugly. - // - class DIV_COUNTER - { - public: - DIV_COUNTER (size_t c, const char* s, const char* p) - : count_ (c), singular_ (s), plural_ (p) {} - - void - operator() (xml::serializer&) const; - - private: - size_t count_; - const char* singular_; - const char* plural_; - }; - - // Generates package name element. - // - class TR_NAME - { - public: - TR_NAME (const string& n, const string& q, const dir_path& r) - : name_ (n), query_param_ (q), root_ (r) {} - - void - operator() (xml::serializer&) const; - - private: - const string& name_; - const string& query_param_; - const dir_path& root_; - }; - - // Generates package version element. - // - class TR_VERSION - { - public: - // Display the version as a link to the package version details page. - // - TR_VERSION (const string& p, const version& v, const dir_path& r) - : package_ (&p), - version_ (v.string ()), - stub_ (v.compare (wildcard_version, true) == 0), - root_ (&r) - { - } - - // Display the version as a regular text. - // - TR_VERSION (const version& v) - : package_ (nullptr), - version_ (v.string ()), - stub_ (v.compare (wildcard_version, true) == 0), - root_ (nullptr) - { - } - - void - operator() (xml::serializer&) const; - - private: - const string* package_; - string version_; - bool stub_; - const dir_path* root_; - }; - - // Generates package summary element. - // - class TR_SUMMARY - { - public: - TR_SUMMARY (const string& s): summary_ (s) {} - - void - operator() (xml::serializer&) const; - - private: - const string& summary_; - }; - - // Generates package license alternatives element. - // - class TR_LICENSE - { - public: - TR_LICENSE (const license_alternatives& l): licenses_ (l) {} - - void - operator() (xml::serializer&) const; - - private: - const license_alternatives& licenses_; - }; - - // Generates package license alternatives elements. Differs from TR_LICENSE - // by producing multiple rows instead of a single one. - // - class TR_LICENSES - { - public: - TR_LICENSES (const license_alternatives& l): licenses_ (l) {} - - void - operator() (xml::serializer&) const; - - private: - const license_alternatives& licenses_; - }; - - // Generates package tags element. - // - class TR_TAGS - { - public: - TR_TAGS (const strings& ts, const dir_path& r): tags_ (ts), root_ (r) {} - - void - operator() (xml::serializer&) const; - - private: - const strings& tags_; - const dir_path& root_; - }; - - // Generates package dependencies element. - // - class TR_DEPENDS - { - public: - TR_DEPENDS (const dependencies& d, const dir_path& r) - : dependencies_ (d), root_ (r) {} - - void - operator() (xml::serializer&) const; - - private: - const dependencies& dependencies_; - const dir_path& root_; - }; - - // Generates package requirements element. - // - class TR_REQUIRES - { - public: - TR_REQUIRES (const requirements& r): requirements_ (r) {} - - void - operator() (xml::serializer&) const; - - private: - const requirements& requirements_; - }; - - // Generates url element. - // - class TR_URL - { - public: - TR_URL (const url& u, const char* l = "url"): url_ (u), label_ (l) {} - - void - operator() (xml::serializer&) const; - - private: - const url& url_; - const char* label_; - }; - - // Generates email element. - // - class TR_EMAIL - { - public: - TR_EMAIL (const email& e, const char* l = "email") - : email_ (e), label_ (l) {} - - void - operator() (xml::serializer&) const; - - private: - const email& email_; - const char* label_; - }; - - // Generates package version priority element. - // - class TR_PRIORITY - { - public: - TR_PRIORITY (const priority& p): priority_ (p) {} - - void - operator() (xml::serializer&) const; - - private: - const priority& priority_; - }; - - // Generates repository name element. - // - class TR_REPOSITORY - { - public: - TR_REPOSITORY (const string& n, const dir_path& r) - : name_ (n), root_ (r) {} - - void - operator() (xml::serializer&) const; - - private: - const string& name_; - const dir_path& root_; - }; - - // Generates repository location element. - // - class TR_LOCATION - { - public: - TR_LOCATION (const repository_location& l): location_ (l) {} - - void - operator() (xml::serializer&) const; - - private: - const repository_location& location_; - }; - - // Generates package download URL element. - // - class TR_DOWNLOAD - { - public: - TR_DOWNLOAD (const string& u): url_ (u) {} - - void - operator() (xml::serializer&) const; - - private: - const string& url_; - }; - - // Generates sha256sum element. - // - class TR_SHA256SUM - { - public: - TR_SHA256SUM (const string& s): sha256sum_ (s) {} - - void - operator() (xml::serializer&) const; - - private: - const string& sha256sum_; - }; - - // Generates comment element. - // - class SPAN_COMMENT - { - public: - SPAN_COMMENT (const string& c): comment_ (c) {} - - void - operator() (xml::serializer&) const; - - private: - const string& comment_; - }; - - // Generates package description element. - // - class P_DESCRIPTION - { - public: - // Genereate full description. - // - P_DESCRIPTION (const string& d, const string& id = "") - : description_ (d), length_ (d.size ()), url_ (nullptr), id_ (id) {} - - // Genereate brief description. - // - P_DESCRIPTION (const string& d, size_t l, const string& u) - : description_ (d), length_ (l), url_ (&u) {} - - void - operator() (xml::serializer&) const; - - private: - const string& description_; - size_t length_; - const string* url_; // Full page url. - string id_; - }; - - // Generates package description element. - // - class PRE_CHANGES - { - public: - // Genereate full changes info. - // - PRE_CHANGES (const string& c) - : changes_ (c), length_ (c.size ()), url_ (nullptr) {} - - // Genereate brief changes info. - // - PRE_CHANGES (const string& c, size_t l, const string& u) - : changes_ (c), length_ (l), url_ (&u) {} - - void - operator() (xml::serializer&) const; - - private: - const string& changes_; - size_t length_; - const string* url_; // Full page url. - }; - - // Generates paging element. - // - class DIV_PAGER - { - public: - DIV_PAGER (size_t current_page, - size_t item_count, - size_t item_per_page, - size_t page_number_count, - const string& url); - - void - operator() (xml::serializer&) const; - - private: - size_t current_page_; - size_t item_count_; - size_t item_per_page_; - size_t page_number_count_; - const string& url_; - }; - - // Convert the argument to a string representing the valid HTML 5 'id' - // attribute value. - // - string - html_id (const string&); -} - -#endif // MOD_PAGE diff --git a/mod/page.cxx b/mod/page.cxx index 1ee18ce..8d8e6e6 100644 --- a/mod/page.cxx +++ b/mod/page.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include #include // hex, uppercase, right @@ -12,11 +12,11 @@ #include -#include -#include +#include +#include -#include -#include +#include +#include using namespace std; using namespace xml; diff --git a/mod/page.hxx b/mod/page.hxx new file mode 100644 index 0000000..d79860d --- /dev/null +++ b/mod/page.hxx @@ -0,0 +1,428 @@ +// file : mod/page.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_PAGE_HXX +#define MOD_PAGE_HXX + +#include + +#include + +#include +#include + +#include + +#include // page_menu + +namespace brep +{ + // Page common building blocks. + // + + // Generates CSS link elements. + // + class CSS_LINKS + { + public: + CSS_LINKS (const path& p, const dir_path& r): path_ (p), root_ (r) {} + + void + operator() (xml::serializer&) const; + + private: + const path& path_; + const dir_path& root_; + }; + + // Generates page header element. + // + class DIV_HEADER + { + public: + DIV_HEADER (const dir_path& root, + const web::xhtml::fragment& logo, + const vector& menu): + root_ (root), logo_ (logo), menu_ (menu) {} + + void + operator() (xml::serializer&) const; + + private: + const dir_path& root_; + const web::xhtml::fragment& logo_; + const vector& menu_; + }; + + // Generates package search form element. + // + class FORM_SEARCH + { + public: + FORM_SEARCH (const string& q): query_ (q) {} + + void + operator() (xml::serializer&) const; + + private: + const string& query_; + }; + + // Generates counter element. + // + // It could be redunant to distinguish between singular and plural word forms + // if it wouldn't be so cheap in English, and phrase '1 Packages' wouldn't + // look that ugly. + // + class DIV_COUNTER + { + public: + DIV_COUNTER (size_t c, const char* s, const char* p) + : count_ (c), singular_ (s), plural_ (p) {} + + void + operator() (xml::serializer&) const; + + private: + size_t count_; + const char* singular_; + const char* plural_; + }; + + // Generates package name element. + // + class TR_NAME + { + public: + TR_NAME (const string& n, const string& q, const dir_path& r) + : name_ (n), query_param_ (q), root_ (r) {} + + void + operator() (xml::serializer&) const; + + private: + const string& name_; + const string& query_param_; + const dir_path& root_; + }; + + // Generates package version element. + // + class TR_VERSION + { + public: + // Display the version as a link to the package version details page. + // + TR_VERSION (const string& p, const version& v, const dir_path& r) + : package_ (&p), + version_ (v.string ()), + stub_ (v.compare (wildcard_version, true) == 0), + root_ (&r) + { + } + + // Display the version as a regular text. + // + TR_VERSION (const version& v) + : package_ (nullptr), + version_ (v.string ()), + stub_ (v.compare (wildcard_version, true) == 0), + root_ (nullptr) + { + } + + void + operator() (xml::serializer&) const; + + private: + const string* package_; + string version_; + bool stub_; + const dir_path* root_; + }; + + // Generates package summary element. + // + class TR_SUMMARY + { + public: + TR_SUMMARY (const string& s): summary_ (s) {} + + void + operator() (xml::serializer&) const; + + private: + const string& summary_; + }; + + // Generates package license alternatives element. + // + class TR_LICENSE + { + public: + TR_LICENSE (const license_alternatives& l): licenses_ (l) {} + + void + operator() (xml::serializer&) const; + + private: + const license_alternatives& licenses_; + }; + + // Generates package license alternatives elements. Differs from TR_LICENSE + // by producing multiple rows instead of a single one. + // + class TR_LICENSES + { + public: + TR_LICENSES (const license_alternatives& l): licenses_ (l) {} + + void + operator() (xml::serializer&) const; + + private: + const license_alternatives& licenses_; + }; + + // Generates package tags element. + // + class TR_TAGS + { + public: + TR_TAGS (const strings& ts, const dir_path& r): tags_ (ts), root_ (r) {} + + void + operator() (xml::serializer&) const; + + private: + const strings& tags_; + const dir_path& root_; + }; + + // Generates package dependencies element. + // + class TR_DEPENDS + { + public: + TR_DEPENDS (const dependencies& d, const dir_path& r) + : dependencies_ (d), root_ (r) {} + + void + operator() (xml::serializer&) const; + + private: + const dependencies& dependencies_; + const dir_path& root_; + }; + + // Generates package requirements element. + // + class TR_REQUIRES + { + public: + TR_REQUIRES (const requirements& r): requirements_ (r) {} + + void + operator() (xml::serializer&) const; + + private: + const requirements& requirements_; + }; + + // Generates url element. + // + class TR_URL + { + public: + TR_URL (const url& u, const char* l = "url"): url_ (u), label_ (l) {} + + void + operator() (xml::serializer&) const; + + private: + const url& url_; + const char* label_; + }; + + // Generates email element. + // + class TR_EMAIL + { + public: + TR_EMAIL (const email& e, const char* l = "email") + : email_ (e), label_ (l) {} + + void + operator() (xml::serializer&) const; + + private: + const email& email_; + const char* label_; + }; + + // Generates package version priority element. + // + class TR_PRIORITY + { + public: + TR_PRIORITY (const priority& p): priority_ (p) {} + + void + operator() (xml::serializer&) const; + + private: + const priority& priority_; + }; + + // Generates repository name element. + // + class TR_REPOSITORY + { + public: + TR_REPOSITORY (const string& n, const dir_path& r) + : name_ (n), root_ (r) {} + + void + operator() (xml::serializer&) const; + + private: + const string& name_; + const dir_path& root_; + }; + + // Generates repository location element. + // + class TR_LOCATION + { + public: + TR_LOCATION (const repository_location& l): location_ (l) {} + + void + operator() (xml::serializer&) const; + + private: + const repository_location& location_; + }; + + // Generates package download URL element. + // + class TR_DOWNLOAD + { + public: + TR_DOWNLOAD (const string& u): url_ (u) {} + + void + operator() (xml::serializer&) const; + + private: + const string& url_; + }; + + // Generates sha256sum element. + // + class TR_SHA256SUM + { + public: + TR_SHA256SUM (const string& s): sha256sum_ (s) {} + + void + operator() (xml::serializer&) const; + + private: + const string& sha256sum_; + }; + + // Generates comment element. + // + class SPAN_COMMENT + { + public: + SPAN_COMMENT (const string& c): comment_ (c) {} + + void + operator() (xml::serializer&) const; + + private: + const string& comment_; + }; + + // Generates package description element. + // + class P_DESCRIPTION + { + public: + // Genereate full description. + // + P_DESCRIPTION (const string& d, const string& id = "") + : description_ (d), length_ (d.size ()), url_ (nullptr), id_ (id) {} + + // Genereate brief description. + // + P_DESCRIPTION (const string& d, size_t l, const string& u) + : description_ (d), length_ (l), url_ (&u) {} + + void + operator() (xml::serializer&) const; + + private: + const string& description_; + size_t length_; + const string* url_; // Full page url. + string id_; + }; + + // Generates package description element. + // + class PRE_CHANGES + { + public: + // Genereate full changes info. + // + PRE_CHANGES (const string& c) + : changes_ (c), length_ (c.size ()), url_ (nullptr) {} + + // Genereate brief changes info. + // + PRE_CHANGES (const string& c, size_t l, const string& u) + : changes_ (c), length_ (l), url_ (&u) {} + + void + operator() (xml::serializer&) const; + + private: + const string& changes_; + size_t length_; + const string* url_; // Full page url. + }; + + // Generates paging element. + // + class DIV_PAGER + { + public: + DIV_PAGER (size_t current_page, + size_t item_count, + size_t item_per_page, + size_t page_number_count, + const string& url); + + void + operator() (xml::serializer&) const; + + private: + size_t current_page_; + size_t item_count_; + size_t item_per_page_; + size_t page_number_count_; + const string& url_; + }; + + // Convert the argument to a string representing the valid HTML 5 'id' + // attribute value. + // + string + html_id (const string&); +} + +#endif // MOD_PAGE_HXX diff --git a/mod/services.cxx b/mod/services.cxx index fa788b3..dfa14a8 100644 --- a/mod/services.cxx +++ b/mod/services.cxx @@ -4,12 +4,12 @@ #include // AP_MODULE_DECLARE_DATA -#include +#include -#include -#include +#include +#include -#include +#include static brep::repository_root mod; web::apache::service AP_MODULE_DECLARE_DATA brep_module ("brep", mod); diff --git a/mod/types-parsers b/mod/types-parsers deleted file mode 100644 index eb206e4..0000000 --- a/mod/types-parsers +++ /dev/null @@ -1,64 +0,0 @@ -// file : mod/types-parsers -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -// CLI parsers, included into the generated source files. -// - -#ifndef MOD_TYPES_PARSERS -#define MOD_TYPES_PARSERS - -#include - -#include -#include - -#include - -namespace brep -{ - namespace cli - { - class scanner; - - template - struct parser; - - template <> - struct parser - { - static void - parse (path&, bool&, scanner&); - }; - - template <> - struct parser - { - static void - parse (dir_path&, bool&, scanner&); - }; - - template <> - struct parser - { - static void - parse (page_form&, bool&, scanner&); - }; - - template <> - struct parser - { - static void - parse (page_menu&, bool&, scanner&); - }; - - template <> - struct parser - { - static void - parse (web::xhtml::fragment&, bool&, scanner&); - }; - } -} - -#endif // MOD_TYPES_PARSERS diff --git a/mod/types-parsers.cxx b/mod/types-parsers.cxx index fb293a3..65d9c6c 100644 --- a/mod/types-parsers.cxx +++ b/mod/types-parsers.cxx @@ -2,9 +2,9 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include -#include +#include using namespace std; using namespace web::xhtml; diff --git a/mod/types-parsers.hxx b/mod/types-parsers.hxx new file mode 100644 index 0000000..03c54fe --- /dev/null +++ b/mod/types-parsers.hxx @@ -0,0 +1,64 @@ +// file : mod/types-parsers.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +// CLI parsers, included into the generated source files. +// + +#ifndef MOD_TYPES_PARSERS_HXX +#define MOD_TYPES_PARSERS_HXX + +#include + +#include +#include + +#include + +namespace brep +{ + namespace cli + { + class scanner; + + template + struct parser; + + template <> + struct parser + { + static void + parse (path&, bool&, scanner&); + }; + + template <> + struct parser + { + static void + parse (dir_path&, bool&, scanner&); + }; + + template <> + struct parser + { + static void + parse (page_form&, bool&, scanner&); + }; + + template <> + struct parser + { + static void + parse (page_menu&, bool&, scanner&); + }; + + template <> + struct parser + { + static void + parse (web::xhtml::fragment&, bool&, scanner&); + }; + } +} + +#endif // MOD_TYPES_PARSERS_HXX diff --git a/tests/load/buildfile b/tests/load/buildfile index 2088ee1..6b81346 100644 --- a/tests/load/buildfile +++ b/tests/load/buildfile @@ -7,9 +7,9 @@ import libs += libbutl%lib{butl} import libs += libodb-pgsql%lib{odb-pgsql} import libs += libodb%lib{odb} -include ../../brep/ +include ../../libbrep/ -exe{driver}: cxx{driver} ../../brep/lib{brep} $libs +exe{driver}: cxx{driver} ../../libbrep/lib{brep} $libs # Disable for now until build2 test module supports running custom # commands, pre/post steps. diff --git a/tests/load/driver.cxx b/tests/load/driver.cxx index 24b29b4..7c8662b 100644 --- a/tests/load/driver.cxx +++ b/tests/load/driver.cxx @@ -14,11 +14,11 @@ #include #include -#include -#include +#include +#include -#include -#include +#include +#include using namespace std; using namespace odb::core; diff --git a/tests/web/xhtml/driver.cxx b/tests/web/xhtml/driver.cxx index e50a9a7..7d9a536 100644 --- a/tests/web/xhtml/driver.cxx +++ b/tests/web/xhtml/driver.cxx @@ -7,7 +7,7 @@ #include -#include +#include using namespace std; using namespace xml; diff --git a/web/apache/log b/web/apache/log deleted file mode 100644 index dda9099..0000000 --- a/web/apache/log +++ /dev/null @@ -1,81 +0,0 @@ -// file : web/apache/log -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_APACHE_LOG -#define WEB_APACHE_LOG - -#include // request_rec, server_rec -#include -#include // module - -#include // uint64_t -#include // min() - -#include - -namespace web -{ - namespace apache - { - class log: public web::log - { - public: - - log (server_rec* s, const ::module* m) noexcept - : server_ (s), module_ (m) {} - - virtual void - write (const char* msg) {write (APLOG_ERR, msg);} - - // Apache-specific interface. - // - void - write (int level, const char* msg) const noexcept - { - write (nullptr, 0, nullptr, level, msg); - } - - void - write (const char* file, - std::uint64_t line, - const char* func, - int level, - const char* msg) const noexcept - { - if (file && *file) - file = nullptr; // Skip file/line placeholder from log line. - - level = std::min (level, APLOG_TRACE8); - - if (func) - ap_log_error (file, - line, - module_->module_index, - level, - 0, - server_, - "[%s]: %s", - func, - msg); - else - // Skip function name placeholder from log line. - // - ap_log_error (file, - line, - module_->module_index, - level, - 0, - server_, - ": %s", - msg); - } - - private: - server_rec* server_; - const ::module* module_; // Apache module. - }; - } -} - -#endif // WEB_APACHE_LOG diff --git a/web/apache/log.hxx b/web/apache/log.hxx new file mode 100644 index 0000000..4397875 --- /dev/null +++ b/web/apache/log.hxx @@ -0,0 +1,81 @@ +// file : web/apache/log.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_APACHE_LOG_HXX +#define WEB_APACHE_LOG_HXX + +#include // request_rec, server_rec +#include +#include // module + +#include // uint64_t +#include // min() + +#include + +namespace web +{ + namespace apache + { + class log: public web::log + { + public: + + log (server_rec* s, const ::module* m) noexcept + : server_ (s), module_ (m) {} + + virtual void + write (const char* msg) {write (APLOG_ERR, msg);} + + // Apache-specific interface. + // + void + write (int level, const char* msg) const noexcept + { + write (nullptr, 0, nullptr, level, msg); + } + + void + write (const char* file, + std::uint64_t line, + const char* func, + int level, + const char* msg) const noexcept + { + if (file && *file) + file = nullptr; // Skip file/line placeholder from log line. + + level = std::min (level, APLOG_TRACE8); + + if (func) + ap_log_error (file, + line, + module_->module_index, + level, + 0, + server_, + "[%s]: %s", + func, + msg); + else + // Skip function name placeholder from log line. + // + ap_log_error (file, + line, + module_->module_index, + level, + 0, + server_, + ": %s", + msg); + } + + private: + server_rec* server_; + const ::module* module_; // Apache module. + }; + } +} + +#endif // WEB_APACHE_LOG_HXX diff --git a/web/apache/request b/web/apache/request deleted file mode 100644 index a35c5dc..0000000 --- a/web/apache/request +++ /dev/null @@ -1,190 +0,0 @@ -// file : web/apache/request -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_APACHE_REQUEST -#define WEB_APACHE_REQUEST - -#include // request_rec, HTTP_*, OK, M_POST - -#include -#include // unique_ptr -#include -#include -#include -#include - -#include -#include - -namespace web -{ - namespace apache - { - // The state of the request processing, reflecting an interaction with - // Apache API (like reading/writing content function calls), with no - // buffering taken into account. Any state different from the initial - // suppose that some irrevocable interaction with Apache API have - // happened, so request processing should be either completed, or - // reported as failed. State values are ordered in a sense that the - // higher value reflects the more advanced stage of processing, so the - // request current state value may not decrease. - // - enum class request_state - { - // Denotes the initial stage of the request handling. At this stage - // the request line and headers are already parsed by Apache. - // - initial, - - // Reading the request content. - // - reading, - - // Adding the response headers (cookies in particular). - // - headers, - - // Writing the response content. - // - writing - }; - - // Extends istreambuf with read limit checking, caching, etc. (see the - // implementation for details). - // - class istreambuf_cache; - - class request: public web::request, - public web::response, - public stream_state - { - friend class service; - - // Can not be inline/default due to the member of - // unique_ptr type. Note that istreambuf_cache type is - // incomplete. - // - request (request_rec* rec) noexcept; - ~request (); - - request_state - state () const noexcept {return state_;} - - // Flush the buffered response content if present. The returned value - // should be passed to Apache API on request handler exit. - // - int - flush (); - - // Prepare for the request re-processing if possible (no unbuffered - // read/write operations have been done). Throw sequence_error - // otherwise. In particular, the preparation can include the response - // content buffer cleanup, the request content buffer rewind. - // - void - rewind (); - - // Get request path. - // - virtual const path_type& - path (); - - // Get request body data stream. - // - virtual std::istream& - content (size_t limit = 0, size_t buffer = 0); - - // Get request parameters. - // - virtual const name_values& - parameters (); - - // Get request cookies. - // - virtual const name_values& - cookies (); - - // Get response status code. - // - status_code - status () const noexcept {return rec_->status;} - - // Set response status code. - // - virtual void - status (status_code status); - - // Set response status code, content type and get body stream. - // - virtual std::ostream& - content (status_code status, - const std::string& type, - bool buffer = true); - - // Add response cookie. - // - virtual void - cookie (const char* name, - const char* value, - const std::chrono::seconds* max_age = nullptr, - const char* path = nullptr, - const char* domain = nullptr, - bool secure = false, - bool buffer = true); - - private: - // Get application/x-www-form-urlencoded form data. If request::content() - // was not called yet (and so limits are not specified) then set both of - // them to 64KB. Rewind the stream afterwards, so it's available for the - // application as well, unless no buffering were requested beforehand. - // - const std::string& - form_data (); - - void - parse_parameters (const char* args); - - // Advance the request processing state. Noop if new state is equal to - // the current one. Throw sequence_error if the new state is less then - // the current one. Can throw invalid_request if HTTP request is - // malformed. - // - void - state (request_state); - - // stream_state members implementation. - // - virtual void - set_read_state () {state (request_state::reading);} - - virtual void - set_write_state () {state (request_state::writing);} - - // Rewind the input stream (that must exist). Throw sequence_error if - // some unbuffered content have already been read. - // - void - rewind_istream (); - - private: - request_rec* rec_; - request_state state_ = request_state::initial; - - path_type path_; - std::unique_ptr parameters_; - std::unique_ptr cookies_; - std::unique_ptr form_data_; - - std::unique_ptr in_buf_; - std::unique_ptr in_; - - std::unique_ptr out_buf_; - std::unique_ptr out_; - }; - } -} - -#include - -#endif // WEB_APACHE_REQUEST diff --git a/web/apache/request.cxx b/web/apache/request.cxx index b4fb080..219f887 100644 --- a/web/apache/request.cxx +++ b/web/apache/request.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include // apr_table_*, apr_array_header_t #include // apr_pstrdup() @@ -31,7 +31,7 @@ #include #include -#include +#include using namespace std; using namespace butl; diff --git a/web/apache/request.hxx b/web/apache/request.hxx new file mode 100644 index 0000000..0488fb2 --- /dev/null +++ b/web/apache/request.hxx @@ -0,0 +1,190 @@ +// file : web/apache/request.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_APACHE_REQUEST_HXX +#define WEB_APACHE_REQUEST_HXX + +#include // request_rec, HTTP_*, OK, M_POST + +#include +#include // unique_ptr +#include +#include +#include +#include + +#include +#include + +namespace web +{ + namespace apache + { + // The state of the request processing, reflecting an interaction with + // Apache API (like reading/writing content function calls), with no + // buffering taken into account. Any state different from the initial + // suppose that some irrevocable interaction with Apache API have + // happened, so request processing should be either completed, or + // reported as failed. State values are ordered in a sense that the + // higher value reflects the more advanced stage of processing, so the + // request current state value may not decrease. + // + enum class request_state + { + // Denotes the initial stage of the request handling. At this stage + // the request line and headers are already parsed by Apache. + // + initial, + + // Reading the request content. + // + reading, + + // Adding the response headers (cookies in particular). + // + headers, + + // Writing the response content. + // + writing + }; + + // Extends istreambuf with read limit checking, caching, etc. (see the + // implementation for details). + // + class istreambuf_cache; + + class request: public web::request, + public web::response, + public stream_state + { + friend class service; + + // Can not be inline/default due to the member of + // unique_ptr type. Note that istreambuf_cache type is + // incomplete. + // + request (request_rec* rec) noexcept; + ~request (); + + request_state + state () const noexcept {return state_;} + + // Flush the buffered response content if present. The returned value + // should be passed to Apache API on request handler exit. + // + int + flush (); + + // Prepare for the request re-processing if possible (no unbuffered + // read/write operations have been done). Throw sequence_error + // otherwise. In particular, the preparation can include the response + // content buffer cleanup, the request content buffer rewind. + // + void + rewind (); + + // Get request path. + // + virtual const path_type& + path (); + + // Get request body data stream. + // + virtual std::istream& + content (size_t limit = 0, size_t buffer = 0); + + // Get request parameters. + // + virtual const name_values& + parameters (); + + // Get request cookies. + // + virtual const name_values& + cookies (); + + // Get response status code. + // + status_code + status () const noexcept {return rec_->status;} + + // Set response status code. + // + virtual void + status (status_code status); + + // Set response status code, content type and get body stream. + // + virtual std::ostream& + content (status_code status, + const std::string& type, + bool buffer = true); + + // Add response cookie. + // + virtual void + cookie (const char* name, + const char* value, + const std::chrono::seconds* max_age = nullptr, + const char* path = nullptr, + const char* domain = nullptr, + bool secure = false, + bool buffer = true); + + private: + // Get application/x-www-form-urlencoded form data. If request::content() + // was not called yet (and so limits are not specified) then set both of + // them to 64KB. Rewind the stream afterwards, so it's available for the + // application as well, unless no buffering were requested beforehand. + // + const std::string& + form_data (); + + void + parse_parameters (const char* args); + + // Advance the request processing state. Noop if new state is equal to + // the current one. Throw sequence_error if the new state is less then + // the current one. Can throw invalid_request if HTTP request is + // malformed. + // + void + state (request_state); + + // stream_state members implementation. + // + virtual void + set_read_state () {state (request_state::reading);} + + virtual void + set_write_state () {state (request_state::writing);} + + // Rewind the input stream (that must exist). Throw sequence_error if + // some unbuffered content have already been read. + // + void + rewind_istream (); + + private: + request_rec* rec_; + request_state state_ = request_state::initial; + + path_type path_; + std::unique_ptr parameters_; + std::unique_ptr cookies_; + std::unique_ptr form_data_; + + std::unique_ptr in_buf_; + std::unique_ptr in_; + + std::unique_ptr out_buf_; + std::unique_ptr out_; + }; + } +} + +#include + +#endif // WEB_APACHE_REQUEST_HXX diff --git a/web/apache/service b/web/apache/service deleted file mode 100644 index 45cf39b..0000000 --- a/web/apache/service +++ /dev/null @@ -1,330 +0,0 @@ -// file : web/apache/service -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_APACHE_SERVICE -#define WEB_APACHE_SERVICE - -#include // apr_pool_t -#include // APR_HOOK_* - -#include // request_rec, server_rec, HTTP_*, DECLINED -#include // module, cmd_parms, ap_hook_*() - -#include -#include // unique_ptr -#include -#include - -#include -#include -#include - -namespace web -{ - namespace apache - { - // Apache has 3 configuration scopes: main server, virtual server, and - // directory (location). It provides configuration scope-aware modules - // with the ability to build a hierarchy of configuration contexts. Later, - // when processing a request, Apache passes the appropriate directory - // configuration context to the request handler. - // - // This Apache service implementation first makes a copy of the provided - // (in the constructor below) module exemplar for each directory context. - // It then initializes each of these "context exemplars" with the (merged) - // set of configuration options. Finally, when handling a request, it - // copies the corresponding "context exemplar" to create the "handling - // instance". Note that the "context exemplars" are created as a copy of - // the provided exemplar, which is never initialized. As a result, it is - // possible to detect if the module's copy constructor is used to create a - // "context exemplar" or a "handling instance". - // - class service: ::module - { - public: - // Note that the module exemplar is stored by-reference. - // - template - service (const std::string& name, M& exemplar) - : ::module - { - STANDARD20_MODULE_STUFF, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - ®ister_hooks - }, - name_ (name), - exemplar_ (exemplar) - { - init_directives (); - - // Set configuration context management hooks. - // - // The overall process of building the configuration hierarchy for a - // module is as follows: - // - // 1. Apache creates directory and server configuration contexts for - // scopes containing module-defined directives by calling the - // create_{server,dir}_context() callback functions. For directives - // at the server scope the special directory context is created as - // well. - // - // 2. Apache calls parse_option() function for each module-defined - // directive. The function parses the directives and places the - // resulting options into the corresponding configuration context. - // It also establishes the directory-server contexts relations. - // - // 3. Apache calls merge_server_context() function for each virtual - // server. The function complements virtual server context options - // with the ones from the main server. - // - // 4. Apache calls config_finalizer() which complements the directory - // contexts options with the ones from the enclosing servers. - // - // 5. Apache calls worker_initializer() which creates module exemplar - // for each directory configuration context that have - // 'SetHandler ' directive in effect for it. - // - // References: - // http://www.apachetutor.org/dev/config - // http://httpd.apache.org/docs/2.4/developer/modguide.html - // http://wiki.apache.org/httpd/ModuleLife - // - create_server_config = &create_server_context; - create_dir_config = &create_dir_context; - merge_server_config = &merge_server_context; - - // instance () is invented to delegate processing from apache - // request handler C function to the service non static member - // function. This appoach resticts number of service objects per - // specific module implementation class with just one instance. - // - service*& srv (instance ()); - assert (srv == nullptr); - srv = this; - } - - ~service () - { - delete [] cmds; - } - - private: - template - static service*& - instance () noexcept - { - static service* instance; - return instance; - } - - template - static void - register_hooks (apr_pool_t*) noexcept - { - // The config_finalizer() function is called at the end of Apache - // server configuration parsing. - // - ap_hook_post_config (&config_finalizer, NULL, NULL, APR_HOOK_LAST); - - // The worker_initializer() function is called right after Apache - // worker process is started. Called for every new process spawned. - // - ap_hook_child_init ( - &worker_initializer, NULL, NULL, APR_HOOK_LAST); - - // The request_handler () function is called for each client request. - // - ap_hook_handler (&request_handler, NULL, NULL, APR_HOOK_LAST); - } - - template - static int - config_finalizer (apr_pool_t*, apr_pool_t*, apr_pool_t*, server_rec* s) - noexcept - { - instance ()->finalize_config (s); - return OK; - } - - template - static void - worker_initializer (apr_pool_t*, server_rec* s) noexcept - { - auto srv (instance ()); - log l (s, srv); - srv->template init_worker (l); - } - - template - static int - request_handler (request_rec* r) noexcept; - - private: - - // Reflects the allowability of the request handling in the specific - // configuration scope. - // - enum class request_handling - { - // Configuration scope has 'SetHandler ' directive - // specified. The module is allowed to handle a request in the scope. - // - allowed, - - // Configuration scope has 'SetHandler |None' - // directive specified. The module is disallowed to handle a request - // in the scope. - // - disallowed, - - // - // Note that if there are several SetHandler directives specified - // in the specific scope, then the latest one takes the precedence. - - // Configuration scope has no SetHandler directive specified. The - // request handling allowability is established by the enclosing - // scopes. - // - inherit - }; - - // Our representation of the Apache configuration context. - // - // The lifetime of this object is under the control of the Apache API, - // which treats it as a raw sequence of bytes. In order not to tinker - // with the C-style structures and APR memory pools, we will keep it a - // (C++11) POD type with just the members required to maintain the - // context hierarchy. - // - // We will then use the pointers to these context objects as keys in - // maps to (1) the corresponding application-level option lists during - // the configuration cycle and to (2) the corresponding module exemplar - // during the HTTP request handling phase. We will also use the same - // type for both directory and server configuration contexts. - // - struct context - { - // Outer (server) configuration context for the directory - // configuration context, NULL otherwise. - // - context* server = nullptr; - - // If module directives appear directly in the server configuration - // scope, Apache creates a special directory context for them. This - // context appears at the same hierarchy level as the user-defined - // directory contexts of the same server scope. - // - bool special; - - // Request handling allowability for the corresponding configuration - // scope. - // - request_handling handling = request_handling::inherit; - - // Create the server configuration context. - // - context (): special (false) {} - - // Create the directory configuration context. Due to the Apache API - // implementation details it is not possible to detect the enclosing - // server configuration context at the time of directory context - // creation. As a result, the server member is set by the module's - // parse_option() function. - // - context (bool s): special (s) {} - - // Ensure the object is only destroyed by Apache. - // - ~context () = delete; - }; - - static context* - context_cast (void* config) noexcept - {return static_cast (config);} - - private: - void - init_directives (); - - // Create the server configuration context. Called by the Apache API - // whenever a new object of that type is required. - // - static void* - create_server_context (apr_pool_t*, server_rec*) noexcept; - - // Create the server directory configuration context. Called by the - // Apache API whenever a new object of that type is required. - // - static void* - create_dir_context (apr_pool_t*, char* dir) noexcept; - - template - static void* - merge_server_context (apr_pool_t*, void* enclosing, void* enclosed) - noexcept - { - instance ()->complement ( - context_cast (enclosed), context_cast (enclosing)); - - return enclosed; - } - - static const char* - parse_option (cmd_parms* parms, void* conf, const char* args) noexcept; - - const char* - add_option (context*, const char* name, optional value); - - void - finalize_config (server_rec*); - - void - clear_config (); - - // Complement the enclosed context with options of the enclosing one. - // If the 'handling' member of the enclosed context is set to - // request_handling::inherit value, assign it a value from the enclosing - // context. - // - void - complement (context* enclosed, context* enclosing); - - template - void - init_worker (log&); - - template - int - handle (request&, const context*, log&) const; - - private: - std::string name_; - module& exemplar_; - option_descriptions option_descriptions_; - - // The context objects pointed to by the key can change during the - // configuration phase. - // - using options = std::map; - options options_; - - // The context objects pointed to by the key can not change during the - // request handling phase. - // - using exemplars = std::map>; - exemplars exemplars_; - - bool options_parsed_ = false; - bool version_logged_ = false; - }; - } -} - -#include - -#endif // WEB_APACHE_SERVICE diff --git a/web/apache/service.cxx b/web/apache/service.cxx index ca8e235..8c3fa82 100644 --- a/web/apache/service.cxx +++ b/web/apache/service.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include // apr_palloc() @@ -18,8 +18,8 @@ #include -#include -#include +#include +#include using namespace std; diff --git a/web/apache/service.hxx b/web/apache/service.hxx new file mode 100644 index 0000000..44d064f --- /dev/null +++ b/web/apache/service.hxx @@ -0,0 +1,330 @@ +// file : web/apache/service.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_APACHE_SERVICE_HXX +#define WEB_APACHE_SERVICE_HXX + +#include // apr_pool_t +#include // APR_HOOK_* + +#include // request_rec, server_rec, HTTP_*, DECLINED +#include // module, cmd_parms, ap_hook_*() + +#include +#include // unique_ptr +#include +#include + +#include +#include +#include + +namespace web +{ + namespace apache + { + // Apache has 3 configuration scopes: main server, virtual server, and + // directory (location). It provides configuration scope-aware modules + // with the ability to build a hierarchy of configuration contexts. Later, + // when processing a request, Apache passes the appropriate directory + // configuration context to the request handler. + // + // This Apache service implementation first makes a copy of the provided + // (in the constructor below) module exemplar for each directory context. + // It then initializes each of these "context exemplars" with the (merged) + // set of configuration options. Finally, when handling a request, it + // copies the corresponding "context exemplar" to create the "handling + // instance". Note that the "context exemplars" are created as a copy of + // the provided exemplar, which is never initialized. As a result, it is + // possible to detect if the module's copy constructor is used to create a + // "context exemplar" or a "handling instance". + // + class service: ::module + { + public: + // Note that the module exemplar is stored by-reference. + // + template + service (const std::string& name, M& exemplar) + : ::module + { + STANDARD20_MODULE_STUFF, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + ®ister_hooks + }, + name_ (name), + exemplar_ (exemplar) + { + init_directives (); + + // Set configuration context management hooks. + // + // The overall process of building the configuration hierarchy for a + // module is as follows: + // + // 1. Apache creates directory and server configuration contexts for + // scopes containing module-defined directives by calling the + // create_{server,dir}_context() callback functions. For directives + // at the server scope the special directory context is created as + // well. + // + // 2. Apache calls parse_option() function for each module-defined + // directive. The function parses the directives and places the + // resulting options into the corresponding configuration context. + // It also establishes the directory-server contexts relations. + // + // 3. Apache calls merge_server_context() function for each virtual + // server. The function complements virtual server context options + // with the ones from the main server. + // + // 4. Apache calls config_finalizer() which complements the directory + // contexts options with the ones from the enclosing servers. + // + // 5. Apache calls worker_initializer() which creates module exemplar + // for each directory configuration context that have + // 'SetHandler ' directive in effect for it. + // + // References: + // http://www.apachetutor.org/dev/config + // http://httpd.apache.org/docs/2.4/developer/modguide.html + // http://wiki.apache.org/httpd/ModuleLife + // + create_server_config = &create_server_context; + create_dir_config = &create_dir_context; + merge_server_config = &merge_server_context; + + // instance () is invented to delegate processing from apache + // request handler C function to the service non static member + // function. This appoach resticts number of service objects per + // specific module implementation class with just one instance. + // + service*& srv (instance ()); + assert (srv == nullptr); + srv = this; + } + + ~service () + { + delete [] cmds; + } + + private: + template + static service*& + instance () noexcept + { + static service* instance; + return instance; + } + + template + static void + register_hooks (apr_pool_t*) noexcept + { + // The config_finalizer() function is called at the end of Apache + // server configuration parsing. + // + ap_hook_post_config (&config_finalizer, NULL, NULL, APR_HOOK_LAST); + + // The worker_initializer() function is called right after Apache + // worker process is started. Called for every new process spawned. + // + ap_hook_child_init ( + &worker_initializer, NULL, NULL, APR_HOOK_LAST); + + // The request_handler () function is called for each client request. + // + ap_hook_handler (&request_handler, NULL, NULL, APR_HOOK_LAST); + } + + template + static int + config_finalizer (apr_pool_t*, apr_pool_t*, apr_pool_t*, server_rec* s) + noexcept + { + instance ()->finalize_config (s); + return OK; + } + + template + static void + worker_initializer (apr_pool_t*, server_rec* s) noexcept + { + auto srv (instance ()); + log l (s, srv); + srv->template init_worker (l); + } + + template + static int + request_handler (request_rec* r) noexcept; + + private: + + // Reflects the allowability of the request handling in the specific + // configuration scope. + // + enum class request_handling + { + // Configuration scope has 'SetHandler ' directive + // specified. The module is allowed to handle a request in the scope. + // + allowed, + + // Configuration scope has 'SetHandler |None' + // directive specified. The module is disallowed to handle a request + // in the scope. + // + disallowed, + + // + // Note that if there are several SetHandler directives specified + // in the specific scope, then the latest one takes the precedence. + + // Configuration scope has no SetHandler directive specified. The + // request handling allowability is established by the enclosing + // scopes. + // + inherit + }; + + // Our representation of the Apache configuration context. + // + // The lifetime of this object is under the control of the Apache API, + // which treats it as a raw sequence of bytes. In order not to tinker + // with the C-style structures and APR memory pools, we will keep it a + // (C++11) POD type with just the members required to maintain the + // context hierarchy. + // + // We will then use the pointers to these context objects as keys in + // maps to (1) the corresponding application-level option lists during + // the configuration cycle and to (2) the corresponding module exemplar + // during the HTTP request handling phase. We will also use the same + // type for both directory and server configuration contexts. + // + struct context + { + // Outer (server) configuration context for the directory + // configuration context, NULL otherwise. + // + context* server = nullptr; + + // If module directives appear directly in the server configuration + // scope, Apache creates a special directory context for them. This + // context appears at the same hierarchy level as the user-defined + // directory contexts of the same server scope. + // + bool special; + + // Request handling allowability for the corresponding configuration + // scope. + // + request_handling handling = request_handling::inherit; + + // Create the server configuration context. + // + context (): special (false) {} + + // Create the directory configuration context. Due to the Apache API + // implementation details it is not possible to detect the enclosing + // server configuration context at the time of directory context + // creation. As a result, the server member is set by the module's + // parse_option() function. + // + context (bool s): special (s) {} + + // Ensure the object is only destroyed by Apache. + // + ~context () = delete; + }; + + static context* + context_cast (void* config) noexcept + {return static_cast (config);} + + private: + void + init_directives (); + + // Create the server configuration context. Called by the Apache API + // whenever a new object of that type is required. + // + static void* + create_server_context (apr_pool_t*, server_rec*) noexcept; + + // Create the server directory configuration context. Called by the + // Apache API whenever a new object of that type is required. + // + static void* + create_dir_context (apr_pool_t*, char* dir) noexcept; + + template + static void* + merge_server_context (apr_pool_t*, void* enclosing, void* enclosed) + noexcept + { + instance ()->complement ( + context_cast (enclosed), context_cast (enclosing)); + + return enclosed; + } + + static const char* + parse_option (cmd_parms* parms, void* conf, const char* args) noexcept; + + const char* + add_option (context*, const char* name, optional value); + + void + finalize_config (server_rec*); + + void + clear_config (); + + // Complement the enclosed context with options of the enclosing one. + // If the 'handling' member of the enclosed context is set to + // request_handling::inherit value, assign it a value from the enclosing + // context. + // + void + complement (context* enclosed, context* enclosing); + + template + void + init_worker (log&); + + template + int + handle (request&, const context*, log&) const; + + private: + std::string name_; + module& exemplar_; + option_descriptions option_descriptions_; + + // The context objects pointed to by the key can change during the + // configuration phase. + // + using options = std::map; + options options_; + + // The context objects pointed to by the key can not change during the + // request handling phase. + // + using exemplars = std::map>; + exemplars exemplars_; + + bool options_parsed_ = false; + bool version_logged_ = false; + }; + } +} + +#include + +#endif // WEB_APACHE_SERVICE_HXX diff --git a/web/apache/stream b/web/apache/stream deleted file mode 100644 index d4abb4e..0000000 --- a/web/apache/stream +++ /dev/null @@ -1,149 +0,0 @@ -// file : web/apache/stream -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_APACHE_STREAM -#define WEB_APACHE_STREAM - -#include // request_rec, HTTP_* -#include // ap_*() - -#include // streamsize -#include -#include // memmove(), size_t -#include -#include // min(), max() - -#include // invalid_request - -namespace web -{ - namespace apache - { - // Object of a class implementing this interface is intended for keeping - // the state of communication with the client. - // - struct stream_state - { - // Called by istreambuf functions when content is about to be read from - // the client. Can throw invalid_request or sequence_error. - // - virtual void - set_read_state () = 0; - - // Called by ostreambuf functions when some content is about to be - // written to the client. Can throw invalid_request or sequence_error. - // - virtual void - set_write_state () = 0; - }; - - // Base class for ostreambuf and istreambuf. References request and - // communication state structures. - // - class rbuf: public std::streambuf - { - protected: - rbuf (request_rec* r, stream_state& s): rec_ (r), state_ (s) {} - - protected: - request_rec* rec_; - stream_state& state_; - }; - - class ostreambuf: public rbuf - { - public: - ostreambuf (request_rec* r, stream_state& s): rbuf (r, s) {} - - private: - virtual int_type - overflow (int_type c) - { - if (c != traits_type::eof ()) - { - state_.set_write_state (); - - char chr (c); - - // Throwing allows to distinguish comm failure from other IO error - // conditions. - // - if (ap_rwrite (&chr, sizeof (chr), rec_) == -1) - throw invalid_request (HTTP_REQUEST_TIME_OUT); - } - - return c; - } - - virtual std::streamsize - xsputn (const char* s, std::streamsize num) - { - state_.set_write_state (); - - if (ap_rwrite (s, num, rec_) < 0) - throw invalid_request (HTTP_REQUEST_TIME_OUT); - - return num; - } - - virtual int - sync () - { - if (ap_rflush (rec_) < 0) - throw invalid_request (HTTP_REQUEST_TIME_OUT); - - return 0; - } - }; - - class istreambuf: public rbuf - { - public: - istreambuf (request_rec* r, - stream_state& s, - size_t bufsize = 1024, - size_t putback = 1) - : rbuf (r, s), - bufsize_ (std::max (bufsize, (size_t)1)), - putback_ (std::min (putback, bufsize_ - 1)), - buf_ (bufsize_) - { - char* p (buf_.data () + putback_); - setg (p, p, p); - } - - protected: - virtual int_type - underflow () - { - if (gptr () < egptr ()) - return traits_type::to_int_type (*gptr ()); - - state_.set_read_state (); - - size_t pb (std::min ((size_t)(gptr () - eback ()), putback_)); - std::memmove (buf_.data () + putback_ - pb, gptr () - pb, pb); - - char* p (buf_.data () + putback_); - int rb (ap_get_client_block (rec_, p, bufsize_ - putback_)); - - if (rb == 0) - return traits_type::eof (); - - if (rb < 0) - throw invalid_request (HTTP_REQUEST_TIME_OUT); - - setg (p - pb, p, p + rb); - return traits_type::to_int_type (*gptr ()); - } - - protected: - size_t bufsize_; - size_t putback_; - std::vector buf_; - }; - } -} - -#endif // WEB_APACHE_STREAM diff --git a/web/apache/stream.hxx b/web/apache/stream.hxx new file mode 100644 index 0000000..3bb422d --- /dev/null +++ b/web/apache/stream.hxx @@ -0,0 +1,149 @@ +// file : web/apache/stream.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_APACHE_STREAM_HXX +#define WEB_APACHE_STREAM_HXX + +#include // request_rec, HTTP_* +#include // ap_*() + +#include // streamsize +#include +#include // memmove(), size_t +#include +#include // min(), max() + +#include // invalid_request + +namespace web +{ + namespace apache + { + // Object of a class implementing this interface is intended for keeping + // the state of communication with the client. + // + struct stream_state + { + // Called by istreambuf functions when content is about to be read from + // the client. Can throw invalid_request or sequence_error. + // + virtual void + set_read_state () = 0; + + // Called by ostreambuf functions when some content is about to be + // written to the client. Can throw invalid_request or sequence_error. + // + virtual void + set_write_state () = 0; + }; + + // Base class for ostreambuf and istreambuf. References request and + // communication state structures. + // + class rbuf: public std::streambuf + { + protected: + rbuf (request_rec* r, stream_state& s): rec_ (r), state_ (s) {} + + protected: + request_rec* rec_; + stream_state& state_; + }; + + class ostreambuf: public rbuf + { + public: + ostreambuf (request_rec* r, stream_state& s): rbuf (r, s) {} + + private: + virtual int_type + overflow (int_type c) + { + if (c != traits_type::eof ()) + { + state_.set_write_state (); + + char chr (c); + + // Throwing allows to distinguish comm failure from other IO error + // conditions. + // + if (ap_rwrite (&chr, sizeof (chr), rec_) == -1) + throw invalid_request (HTTP_REQUEST_TIME_OUT); + } + + return c; + } + + virtual std::streamsize + xsputn (const char* s, std::streamsize num) + { + state_.set_write_state (); + + if (ap_rwrite (s, num, rec_) < 0) + throw invalid_request (HTTP_REQUEST_TIME_OUT); + + return num; + } + + virtual int + sync () + { + if (ap_rflush (rec_) < 0) + throw invalid_request (HTTP_REQUEST_TIME_OUT); + + return 0; + } + }; + + class istreambuf: public rbuf + { + public: + istreambuf (request_rec* r, + stream_state& s, + size_t bufsize = 1024, + size_t putback = 1) + : rbuf (r, s), + bufsize_ (std::max (bufsize, (size_t)1)), + putback_ (std::min (putback, bufsize_ - 1)), + buf_ (bufsize_) + { + char* p (buf_.data () + putback_); + setg (p, p, p); + } + + protected: + virtual int_type + underflow () + { + if (gptr () < egptr ()) + return traits_type::to_int_type (*gptr ()); + + state_.set_read_state (); + + size_t pb (std::min ((size_t)(gptr () - eback ()), putback_)); + std::memmove (buf_.data () + putback_ - pb, gptr () - pb, pb); + + char* p (buf_.data () + putback_); + int rb (ap_get_client_block (rec_, p, bufsize_ - putback_)); + + if (rb == 0) + return traits_type::eof (); + + if (rb < 0) + throw invalid_request (HTTP_REQUEST_TIME_OUT); + + setg (p - pb, p, p + rb); + return traits_type::to_int_type (*gptr ()); + } + + protected: + size_t bufsize_; + size_t putback_; + std::vector buf_; + }; + } +} + +#endif // WEB_APACHE_STREAM_HXX diff --git a/web/mime-url-encoding b/web/mime-url-encoding deleted file mode 100644 index 9db0887..0000000 --- a/web/mime-url-encoding +++ /dev/null @@ -1,30 +0,0 @@ -// file : web/mime-url-encoding -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_MIME_URL_ENCODING -#define WEB_MIME_URL_ENCODING - -#include -#include - -namespace web -{ - // @@ Add the query flag (true by default). If true, then the encoding is - // applied to the URL query part, and so the plus character is used to - // encode the space character. Audit use cases afterwards. - // - void - mime_url_encode (const char* v, std::ostream& o); - - std::string - mime_url_encode (const char* v); - - std::string - mime_url_encode (const std::string& v); - - std::string - mime_url_decode (const char* b, const char* e, bool trim = false); -} - -#endif // WEB_MIME_URL_ENCODING diff --git a/web/mime-url-encoding.cxx b/web/mime-url-encoding.cxx index fa85dd3..e0022bc 100644 --- a/web/mime-url-encoding.cxx +++ b/web/mime-url-encoding.cxx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include +#include #include // hex, uppercase, right #include diff --git a/web/mime-url-encoding.hxx b/web/mime-url-encoding.hxx new file mode 100644 index 0000000..d45f4ca --- /dev/null +++ b/web/mime-url-encoding.hxx @@ -0,0 +1,30 @@ +// file : web/mime-url-encoding.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_MIME_URL_ENCODING_HXX +#define WEB_MIME_URL_ENCODING_HXX + +#include +#include + +namespace web +{ + // @@ Add the query flag (true by default). If true, then the encoding is + // applied to the URL query part, and so the plus character is used to + // encode the space character. Audit use cases afterwards. + // + void + mime_url_encode (const char* v, std::ostream& o); + + std::string + mime_url_encode (const char* v); + + std::string + mime_url_encode (const std::string& v); + + std::string + mime_url_decode (const char* b, const char* e, bool trim = false); +} + +#endif // WEB_MIME_URL_ENCODING_HXX diff --git a/web/module b/web/module deleted file mode 100644 index 1e588a4..0000000 --- a/web/module +++ /dev/null @@ -1,267 +0,0 @@ -// file : web/module -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_MODULE -#define WEB_MODULE - -#include -#include -#include -#include -#include -#include // uint16_t -#include // size_t -#include // move() -#include // runtime_error - -#include -#include - -namespace web -{ - using butl::optional; - - // HTTP status code. - // - // @@ Define some commonly used constants? - // - using status_code = std::uint16_t; - - // This exception is used to signal that the request is invalid - // (4XX codes) rather than that it could not be processed (5XX). - // By default 400 is returned, which means the request is malformed. - // - // If caught by the web server implementation, it will try to return - // the specified status and content to the client, if possible. - // It is, however, may not be possible if some unbuffered content has - // already been written. The behavior in this case is implementation- - // specific and may result in no indication of an error being sent to - // the client. - // - struct invalid_request - { - status_code status; - std::string content; - std::string type; - - //@@ Maybe optional "try again" link? - // - invalid_request (status_code s = 400, - std::string c = "", - std::string t = "text/plain;charset=utf-8") - : status (s), content (std::move (c)), type (std::move (t)) {} - }; - - // Exception indicating HTTP request/response sequencing error. - // For example, trying to change the status code after some - // content has already been written. - // - struct sequence_error: std::runtime_error - { - sequence_error (std::string d): std::runtime_error (std::move (d)) {} - }; - - // Map of module configuration option names to the boolean flag indicating - // whether the value is expected for the option. - // - using option_descriptions = std::map; - - struct name_value - { - // These should eventually become string_view's. - // - std::string name; - optional value; - - name_value () {} - name_value (std::string n, optional v) - : name (std::move (n)), value (std::move (v)) {} - }; - - using name_values = std::vector; - using butl::path; - - class request - { - public: - using path_type = web::path; - - virtual - ~request () = default; - - // Corresponds to abs_path portion of HTTP URL as described in - // "3.2.2 HTTP URL" of http://tools.ietf.org/html/rfc2616. - // Returns '/' if no abs_path is present in URL. - // - virtual const path_type& - path () = 0; - - //@@ Why not pass parameters directly? Lazy parsing? - //@@ Why not have something like operator[] for lookup? Probably - // in name_values. - //@@ Maybe parameter_list() and parameter_map()? - // - // Throw invalid_request if decoding of any name or value fails. - // - virtual const name_values& - parameters () = 0; - - // Throw invalid_request if cookies are malformed. - // - virtual const name_values& - cookies () = 0; - - // Get the stream to read the request content from. If the limit argument - // is zero, then the content limit is left unchanged (unlimited initially). - // Otherwise the requested limit is set, and the invalid_request exception - // with the code 413 (payload too large) will be thrown when the specified - // limit is reached while reading from the stream. If the buffer argument - // is zero, then the buffer size is left unchanged (zero initially). If it - // is impossible to increase the buffer size (because, for example, some - // content is already read unbuffered), then the sequence_error is thrown. - // - // Note that unread input content is discarded when any unbuffered content - // is written, and any attempt to read it will result in the - // sequence_error exception being thrown. - // - virtual std::istream& - content (size_t limit = 0, size_t buffer = 0) = 0; - }; - - class response - { - public: - virtual - ~response () = default; - - // Set status code, content type, and get the stream to write - // the content to. If the buffer argument is true (default), - // then buffer the entire content before sending it as a - // response. This allows us to change the status code in - // case of an error. - // - // Specifically, if there is already content in the buffer - // and the status code is changed, then the old content is - // discarded. If the content was not buffered and the status - // is changed, then the sequence_error exception is thrown. - // If this exception leaves module::handle(), then the - // implementation shall terminate the response in a suitable - // but unspecified manner. In particular, there is no guarantee - // that the user will be notified of an error or observe the - // new status. - // - virtual std::ostream& - content (status_code code = 200, - const std::string& type = "application/xhtml+xml;charset=utf-8", - bool buffer = true) = 0; - - // Set status code without writing any content. On status change, - // discard buffered content or throw sequence_error if content was - // not buffered. - // - virtual void - status (status_code) = 0; - - // Throw sequence_error if some unbuffered content has already - // been written. - // - virtual void - cookie (const char* name, - const char* value, - const std::chrono::seconds* max_age = nullptr, - const char* path = nullptr, - const char* domain = nullptr, - bool secure = false, - bool buffer = true) = 0; - }; - - // A web server logging backend. The module can use it to log - // diagnostics that is meant for the web server operator rather - // than the user. - // - // The module can cast this basic interface to the web server's - // specific implementation that may provide a richer interface. - // - class log - { - public: - virtual - ~log () = default; - - virtual void - write (const char* msg) = 0; - }; - - // The web server creates a new module instance for each request - // by copy-initializing it with the module exemplar. This way we - // achieve two things: we can freely use module data members - // without worrying about multi-threading issues and we - // automatically get started with the initial state for each - // request. If you really need to share some rw-data between - // all the modules, use static data members with appropriate - // locking. See the header in one of the web server - // directories (e.g., apache/) if you need to see the code that - // does this. - // - class module - { - public: - virtual - ~module () = default; - - // Description of configuration options supported by this module. Note: - // should be callable during static initialization. - // - virtual option_descriptions - options () = 0; - - // During startup the web server calls this function on the module - // exemplar to log the module version information. It is up to the web - // server whether to call this function once per module implementation - // type. Therefore, it is expected that this function will log the same - // information for all the module exemplars. - // - virtual void - version (log&) = 0; - - // During startup the web server calls this function on the module - // exemplar passing a list of configuration options. The place these - // configuration options come from is implementation-specific (normally - // a configuration file). The web server guarantees that only options - // listed in the map returned by the options() function above can be - // present. Any exception thrown by this function terminates the web - // server. - // - virtual void - init (const name_values&, log&) = 0; - - // Return false if decline to handle the request. If handling have been - // declined after any unbuffered content has been written, then the - // implementation shall terminate the response in a suitable but - // unspecified manner. - // - // Throw retry if need to retry handling the request. The retry will - // happen on the same instance of the module and the implementation is - // expected to "rewind" the request and response objects to their initial - // state. This is only guaranteed to be possible if the relevant functions - // in the request and response objects were called in buffered mode (the - // buffer argument was true). - // - // Any exception other than retry and invalid_request described above that - // leaves this function is treated by the web server implementation as an - // internal server error (500). Similar to invalid_request, it will try to - // return the status and description (obtained by calling what() on - // std::exception) to the client, if possible. The description is assume - // to be encoded in UTF-8. The implementation may provide a configuration - // option to omit the description from the response, for security/privacy - // reasons. - // - struct retry {}; - - virtual bool - handle (request&, response&, log&) = 0; - }; -} - -#endif // WEB_MODULE diff --git a/web/module.hxx b/web/module.hxx new file mode 100644 index 0000000..1864673 --- /dev/null +++ b/web/module.hxx @@ -0,0 +1,267 @@ +// file : web/module.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_MODULE_HXX +#define WEB_MODULE_HXX + +#include +#include +#include +#include +#include +#include // uint16_t +#include // size_t +#include // move() +#include // runtime_error + +#include +#include + +namespace web +{ + using butl::optional; + + // HTTP status code. + // + // @@ Define some commonly used constants? + // + using status_code = std::uint16_t; + + // This exception is used to signal that the request is invalid + // (4XX codes) rather than that it could not be processed (5XX). + // By default 400 is returned, which means the request is malformed. + // + // If caught by the web server implementation, it will try to return + // the specified status and content to the client, if possible. + // It is, however, may not be possible if some unbuffered content has + // already been written. The behavior in this case is implementation- + // specific and may result in no indication of an error being sent to + // the client. + // + struct invalid_request + { + status_code status; + std::string content; + std::string type; + + //@@ Maybe optional "try again" link? + // + invalid_request (status_code s = 400, + std::string c = "", + std::string t = "text/plain;charset=utf-8") + : status (s), content (std::move (c)), type (std::move (t)) {} + }; + + // Exception indicating HTTP request/response sequencing error. + // For example, trying to change the status code after some + // content has already been written. + // + struct sequence_error: std::runtime_error + { + sequence_error (std::string d): std::runtime_error (std::move (d)) {} + }; + + // Map of module configuration option names to the boolean flag indicating + // whether the value is expected for the option. + // + using option_descriptions = std::map; + + struct name_value + { + // These should eventually become string_view's. + // + std::string name; + optional value; + + name_value () {} + name_value (std::string n, optional v) + : name (std::move (n)), value (std::move (v)) {} + }; + + using name_values = std::vector; + using butl::path; + + class request + { + public: + using path_type = web::path; + + virtual + ~request () = default; + + // Corresponds to abs_path portion of HTTP URL as described in + // "3.2.2 HTTP URL" of http://tools.ietf.org/html/rfc2616. + // Returns '/' if no abs_path is present in URL. + // + virtual const path_type& + path () = 0; + + //@@ Why not pass parameters directly? Lazy parsing? + //@@ Why not have something like operator[] for lookup? Probably + // in name_values. + //@@ Maybe parameter_list() and parameter_map()? + // + // Throw invalid_request if decoding of any name or value fails. + // + virtual const name_values& + parameters () = 0; + + // Throw invalid_request if cookies are malformed. + // + virtual const name_values& + cookies () = 0; + + // Get the stream to read the request content from. If the limit argument + // is zero, then the content limit is left unchanged (unlimited initially). + // Otherwise the requested limit is set, and the invalid_request exception + // with the code 413 (payload too large) will be thrown when the specified + // limit is reached while reading from the stream. If the buffer argument + // is zero, then the buffer size is left unchanged (zero initially). If it + // is impossible to increase the buffer size (because, for example, some + // content is already read unbuffered), then the sequence_error is thrown. + // + // Note that unread input content is discarded when any unbuffered content + // is written, and any attempt to read it will result in the + // sequence_error exception being thrown. + // + virtual std::istream& + content (size_t limit = 0, size_t buffer = 0) = 0; + }; + + class response + { + public: + virtual + ~response () = default; + + // Set status code, content type, and get the stream to write + // the content to. If the buffer argument is true (default), + // then buffer the entire content before sending it as a + // response. This allows us to change the status code in + // case of an error. + // + // Specifically, if there is already content in the buffer + // and the status code is changed, then the old content is + // discarded. If the content was not buffered and the status + // is changed, then the sequence_error exception is thrown. + // If this exception leaves module::handle(), then the + // implementation shall terminate the response in a suitable + // but unspecified manner. In particular, there is no guarantee + // that the user will be notified of an error or observe the + // new status. + // + virtual std::ostream& + content (status_code code = 200, + const std::string& type = "application/xhtml+xml;charset=utf-8", + bool buffer = true) = 0; + + // Set status code without writing any content. On status change, + // discard buffered content or throw sequence_error if content was + // not buffered. + // + virtual void + status (status_code) = 0; + + // Throw sequence_error if some unbuffered content has already + // been written. + // + virtual void + cookie (const char* name, + const char* value, + const std::chrono::seconds* max_age = nullptr, + const char* path = nullptr, + const char* domain = nullptr, + bool secure = false, + bool buffer = true) = 0; + }; + + // A web server logging backend. The module can use it to log + // diagnostics that is meant for the web server operator rather + // than the user. + // + // The module can cast this basic interface to the web server's + // specific implementation that may provide a richer interface. + // + class log + { + public: + virtual + ~log () = default; + + virtual void + write (const char* msg) = 0; + }; + + // The web server creates a new module instance for each request + // by copy-initializing it with the module exemplar. This way we + // achieve two things: we can freely use module data members + // without worrying about multi-threading issues and we + // automatically get started with the initial state for each + // request. If you really need to share some rw-data between + // all the modules, use static data members with appropriate + // locking. See the header in one of the web server + // directories (e.g., apache/) if you need to see the code that + // does this. + // + class module + { + public: + virtual + ~module () = default; + + // Description of configuration options supported by this module. Note: + // should be callable during static initialization. + // + virtual option_descriptions + options () = 0; + + // During startup the web server calls this function on the module + // exemplar to log the module version information. It is up to the web + // server whether to call this function once per module implementation + // type. Therefore, it is expected that this function will log the same + // information for all the module exemplars. + // + virtual void + version (log&) = 0; + + // During startup the web server calls this function on the module + // exemplar passing a list of configuration options. The place these + // configuration options come from is implementation-specific (normally + // a configuration file). The web server guarantees that only options + // listed in the map returned by the options() function above can be + // present. Any exception thrown by this function terminates the web + // server. + // + virtual void + init (const name_values&, log&) = 0; + + // Return false if decline to handle the request. If handling have been + // declined after any unbuffered content has been written, then the + // implementation shall terminate the response in a suitable but + // unspecified manner. + // + // Throw retry if need to retry handling the request. The retry will + // happen on the same instance of the module and the implementation is + // expected to "rewind" the request and response objects to their initial + // state. This is only guaranteed to be possible if the relevant functions + // in the request and response objects were called in buffered mode (the + // buffer argument was true). + // + // Any exception other than retry and invalid_request described above that + // leaves this function is treated by the web server implementation as an + // internal server error (500). Similar to invalid_request, it will try to + // return the status and description (obtained by calling what() on + // std::exception) to the client, if possible. The description is assume + // to be encoded in UTF-8. The implementation may provide a configuration + // option to omit the description from the response, for security/privacy + // reasons. + // + struct retry {}; + + virtual bool + handle (request&, response&, log&) = 0; + }; +} + +#endif // WEB_MODULE_HXX diff --git a/web/xhtml b/web/xhtml deleted file mode 100644 index 58dee7b..0000000 --- a/web/xhtml +++ /dev/null @@ -1,351 +0,0 @@ -// file : web/xhtml -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef WEB_XHTML -#define WEB_XHTML - -#include - -namespace web -{ - // "Canonical" XHTML5 vocabulary. - // - // * One-letter tag names and local variable clash problem - // - // a at|an|an anc anch - // b bt|bo|bl bld bold - // i it|it|it itl ital - // p pt|pr|pr par para - // q qt|qu|qt quo quot - // s st|st|st stk strk - // u ut|un|un unl undr - // - // Other options: - // - _a, a_, xa - // - A, I - // - x::i - // - user-defined literals: "a"_e, "/a"_e, "id"_a - // - // Things can actually get much worse, consider: - // - // int i; - // s << i << "text" << ~i; - // - // So perhaps this is the situation where the explicit namespace - // qualification (e.g., x::p) is the only robust option? - // - // - // * Element/attribute name clash problem (e.g., STYLE) - // - // - some attribute/element name decorator (STYLEA, STYLE_A, STYLE_) - // - rename attribute/element (e.g., STYLEDEF or CSSSTYLE[adds TYPE]); - // in case of STYLE we should probably rename the element since - // attribute will be much more frequently used. - // - "scope" attributes inside elements (P::STYLE); somewhat - // burdensome: P(P::STYLE); could then use low-case names - // for attributes - // - "scope" elements inside other elements (HEAD::STYLE); also - // burdensome. - // - // - // * Text wrapping/indentation - // - // For some (inline) elements we want additional indentation: - // - // 1. Indent content on newline (e.g., for