From 84adf9551bf84d7a5f30e44ddfb59dd8337eeddc Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 24 Dec 2024 22:31:34 +0100 Subject: [PATCH] Hello libcode-seafire-representation --- .editorconfig | 17 ++ .gitattributes | 1 + .gitea/workflows/on-push.yaml | 24 ++ .gitignore | 32 ++ LICENSE | 31 ++ README.md | 21 ++ build/.gitignore | 4 + build/bootstrap.build | 7 + build/export.build | 6 + build/root.build | 16 + buildfile | 5 + code/seafire/representation/.gitignore | 9 + code/seafire/representation/buildfile | 67 ++++ code/seafire/representation/concepts.hxx | 20 ++ .../representation/content-negotiable.hxx | 56 ++++ .../representation/content-negotiable.txx | 86 ++++++ code/seafire/representation/metadata.hxx | 64 ++++ code/seafire/representation/negotiate.hxx | 29 ++ code/seafire/representation/negotiate.txx | 33 ++ .../seafire/representation/representation.cxx | 38 +++ .../seafire/representation/representation.hxx | 101 ++++++ .../seafire/representation/representation.txx | 54 ++++ code/seafire/representation/select.hxx | 23 ++ code/seafire/representation/select.txx | 22 ++ code/seafire/representation/send.hxx | 27 ++ code/seafire/representation/send.txx | 36 +++ code/seafire/representation/traits.hxx | 288 ++++++++++++++++++ code/seafire/representation/version.hxx.in | 37 +++ manifest | 15 + repositories.manifest | 19 ++ tests/.gitignore | 8 + tests/build/.gitignore | 4 + tests/build/bootstrap.build | 5 + tests/build/root.build | 16 + tests/buildfile | 1 + 35 files changed, 1222 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitea/workflows/on-push.yaml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build/.gitignore create mode 100644 build/bootstrap.build create mode 100644 build/export.build create mode 100644 build/root.build create mode 100644 buildfile create mode 100644 code/seafire/representation/.gitignore create mode 100644 code/seafire/representation/buildfile create mode 100644 code/seafire/representation/concepts.hxx create mode 100644 code/seafire/representation/content-negotiable.hxx create mode 100644 code/seafire/representation/content-negotiable.txx create mode 100644 code/seafire/representation/metadata.hxx create mode 100644 code/seafire/representation/negotiate.hxx create mode 100644 code/seafire/representation/negotiate.txx create mode 100644 code/seafire/representation/representation.cxx create mode 100644 code/seafire/representation/representation.hxx create mode 100644 code/seafire/representation/representation.txx create mode 100644 code/seafire/representation/select.hxx create mode 100644 code/seafire/representation/select.txx create mode 100644 code/seafire/representation/send.hxx create mode 100644 code/seafire/representation/send.txx create mode 100644 code/seafire/representation/traits.hxx create mode 100644 code/seafire/representation/version.hxx.in create mode 100644 manifest create mode 100644 repositories.manifest create mode 100644 tests/.gitignore create mode 100644 tests/build/.gitignore create mode 100644 tests/build/bootstrap.build create mode 100644 tests/build/root.build create mode 100644 tests/buildfile diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ec9f3de --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +indent_size = 4 +max_line_length = off +trim_trailing_whitespace = false + +[*.yaml] +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.gitea/workflows/on-push.yaml b/.gitea/workflows/on-push.yaml new file mode 100644 index 0000000..23a9e11 --- /dev/null +++ b/.gitea/workflows/on-push.yaml @@ -0,0 +1,24 @@ +name: on-push +on: [push] + +jobs: + build-and-test: + runs-on: linux + container: code.helloryan.se/infra/buildenv/cxx-amd64-fedora-40:latest + volumes: + - /build + steps: + - name: Clone repository + uses: actions/checkout@v3 + - name: Authenticate + run: | + git config unset http.https://code.helloryan.se/.extraheader + echo "${{ secrets.NETRC }}" >> ~/.netrc + - name: Initialize + run: | + bpkg create -d /build cc config.cc.coptions="-Wall -Werror" + bdep init -A /build + - name: Build + run: b + - name: Test + run: b test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c19e5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +.bdep/ +Doxyfile + +# Local default options files. +# +.build2/local/ + +# Compiler/linker output. +# +*.d +*.t +*.i +*.i.* +*.ii +*.ii.* +*.o +*.obj +*.gcm +*.pcm +*.ifc +*.so +*.dylib +*.dll +*.a +*.lib +*.exp +*.pdb +*.ilk +*.exe +*.exe.dlls/ +*.exe.manifest +*.pc diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dfc745b --- /dev/null +++ b/LICENSE @@ -0,0 +1,31 @@ +Copyright © 2024 Ryan. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. All advertising materials mentioning features or use of this software must + display the following acknowledgement: + + This product includes software developed by Ryan, http://helloryan.se/. + +4. Neither the name(s) of the copyright holder(s) nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..acf40d3 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# libcode-seafire-representation + +![Build status](https://code.helloryan.se/code/libcode-seafire-representation/actions/workflows/on-push.yaml/badge.svg) + +## Requirements + +None, other than a modern C++-compiler. + +## Building + +See the wiki, https://code.helloryan.se/code/wiki/wiki/Build-Instructions, for +build instructions. + +## Contact + +Please report bugs and issues by sending an e-mail to: ryan@helloryan.se. + +## Contributing + +Please send an e-mail to ryan@helloryan.se to request an account and +write-access to the libcode-seafire-representation repository. diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..974e01d --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,4 @@ +/config.build +/root/ +/bootstrap/ +build/ diff --git a/build/bootstrap.build b/build/bootstrap.build new file mode 100644 index 0000000..10c2553 --- /dev/null +++ b/build/bootstrap.build @@ -0,0 +1,7 @@ +project = libcode-seafire-representation + +using version +using config +using test +using install +using dist diff --git a/build/export.build b/build/export.build new file mode 100644 index 0000000..923ad1f --- /dev/null +++ b/build/export.build @@ -0,0 +1,6 @@ +$out_root/ +{ + include code/seafire/representation/ +} + +export $out_root/code/seafire/representation/$import.target diff --git a/build/root.build b/build/root.build new file mode 100644 index 0000000..21e0a2e --- /dev/null +++ b/build/root.build @@ -0,0 +1,16 @@ +# Uncomment to suppress warnings coming from external libraries. +# +#cxx.internal.scope = current + +cxx.std = latest + +using cxx + +hxx{*}: extension = hxx +ixx{*}: extension = ixx +txx{*}: extension = txx +cxx{*}: extension = cxx + +# The test target for cross-testing (running tests under Wine, etc). +# +test.target = $cxx.target diff --git a/buildfile b/buildfile new file mode 100644 index 0000000..bbe185e --- /dev/null +++ b/buildfile @@ -0,0 +1,5 @@ +./: {code/ tests/} doc{README.md} legal{LICENSE} manifest + +# Don't install tests. +# +tests/: install = false diff --git a/code/seafire/representation/.gitignore b/code/seafire/representation/.gitignore new file mode 100644 index 0000000..b1ed0e0 --- /dev/null +++ b/code/seafire/representation/.gitignore @@ -0,0 +1,9 @@ +# Generated version header. +# +version.hxx + +# Unit test executables and Testscript output directories +# (can be symlinks). +# +*.test +test-*.test diff --git a/code/seafire/representation/buildfile b/code/seafire/representation/buildfile new file mode 100644 index 0000000..e5af599 --- /dev/null +++ b/code/seafire/representation/buildfile @@ -0,0 +1,67 @@ +intf_libs = # Interface dependencies. +impl_libs = # Implementation dependencies. + +import intf_libs =+ libasio%lib{asio} +import intf_libs =+ libcode-seafire-common%lib{code-seafire-common} +import intf_libs =+ libcode-seafire-protocol%lib{code-seafire-protocol} +import intf_libs =+ libcode-seafire-server%lib{code-seafire-server} + +./: lib{code-seafire-representation}: libul{code-seafire-representation} + +libul{code-seafire-representation}: {hxx ixx txx cxx}{** -**.test... -version} \ + {hxx }{ version} + +libul{code-seafire-representation}: $impl_libs $intf_libs + +# Unit tests. +# +exe{*.test}: +{ + test = true + install = false +} + +for t: cxx{**.test...} +{ + d = $directory($t) + n = $name($t)... + + ./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n} + $d/exe{$n}: libul{code-seafire-representation}: bin.whole = false +} + +hxx{version}: in{version} $src_root/manifest +{ + dist = true + clean = ($src_root != $out_root) +} + +# Build options. +# +cxx.poptions =+ "-I$out_root" "-I$src_root" + +# Export options. +# +lib{code-seafire-representation}: +{ + cxx.export.poptions = "-I$out_root" "-I$src_root" + cxx.export.libs = $intf_libs +} + +# For pre-releases use the complete version to make sure they cannot +# be used in place of another pre-release or the final version. See +# the version module for details on the version.* variable values. +# +if $version.pre_release + lib{code-seafire-representation}: bin.lib.version = "-$version.project_id" +else + lib{code-seafire-representation}: bin.lib.version = "-$version.major.$version.minor" + +# Install into the code/seafire/representation/ subdirectory of, say, /usr/include/ +# recreating subdirectories. +# +{hxx ixx txx}{*}: +{ + install = include/code/seafire/representation/ + install.subdirs = true +} diff --git a/code/seafire/representation/concepts.hxx b/code/seafire/representation/concepts.hxx new file mode 100644 index 0000000..651273c --- /dev/null +++ b/code/seafire/representation/concepts.hxx @@ -0,0 +1,20 @@ +#ifndef code__seafire__representation__concepts_hxx_ +#define code__seafire__representation__concepts_hxx_ + +#include + +namespace code::seafire::representation +{ + + template + concept BasicRepresentation = traits::is_basic_representation_v; + + template + concept ContentNegotiableRepresentation = traits::is_content_negotiable_representation_v; + + template + concept Representation = traits::is_representation_v; + +} // namespace code::seafire::representation + +#endif diff --git a/code/seafire/representation/content-negotiable.hxx b/code/seafire/representation/content-negotiable.hxx new file mode 100644 index 0000000..371ae6d --- /dev/null +++ b/code/seafire/representation/content-negotiable.hxx @@ -0,0 +1,56 @@ +#ifndef code__seafire__representation__content_negotiable_hxx_ +#define code__seafire__representation__content_negotiable_hxx_ + +#include + +#include + +namespace code::seafire::representation +{ + + template + class content_negotiable_t + : public T + { + public: + using model_type = T; + using model_type::model_type; + + content_negotiable_t(model_type const& other) + : model_type{other} + {} + + content_negotiable_t(model_type&& other) + : model_type{other} + {} + + representation_t + select(std::optional const&) const; + + static + bool + is_accepted(std::optional const&); + + private: + template< + typename CurrentFormat, + typename... MoreFormats + > + representation_t + select(std::optional const&) const; + + template< + typename CurrentFormat, + typename... MoreFormats + > + static + bool + is_accepted(std::optional const&); + + }; + +} // namespace code::seafire::representation + +#include + +#endif diff --git a/code/seafire/representation/content-negotiable.txx b/code/seafire/representation/content-negotiable.txx new file mode 100644 index 0000000..8e5b4ea --- /dev/null +++ b/code/seafire/representation/content-negotiable.txx @@ -0,0 +1,86 @@ +namespace code::seafire::representation +{ + + template< + typename T, + typename... Formats + > + representation_t + content_negotiable_t:: + select(std::optional const& type) const + { + return select(type); + } + + template< + typename T, + typename... Formats + > + bool + content_negotiable_t:: + is_accepted(std::optional const& type) + { + return is_accepted(type); + } + + template< + typename T, + typename... Formats + > + template< + typename CurrentFormat, + typename... MoreFormats + > + representation_t + content_negotiable_t:: + select(std::optional const& type) const + { + // Does the CurrentFormat support serialization of type T? + // + static constexpr bool supported{CurrentFormat::supports_serialization}; + + if constexpr (supported) { + // Can the CurrentFormat produce output accepted by the client? + // + if (!type || CurrentFormat::is_accepted(*type)) { + return CurrentFormat{*this}; + } + } + + if constexpr (sizeof...(MoreFormats) > 0) { + return select(type); + } + + throw "temporary"; // fixme: change + } + + template< + typename T, + typename... Formats + > + template< + typename CurrentFormat, + typename... MoreFormats + > + bool + content_negotiable_t:: + is_accepted(std::optional const& type) + { + // Does the CurrentFormat support serialization of type T? + // + static constexpr bool supported{CurrentFormat::supports_serialization}; + + if constexpr (supported) { + // Can the CurrentFormat produce output accepted by the client? + // + if (!type || CurrentFormat::is_accepted(*type)) + return true; + } + + if constexpr (sizeof...(MoreFormats) > 0) + return is_accepted(type); + + return false; + } + +} // namespace code::seafire::representation diff --git a/code/seafire/representation/metadata.hxx b/code/seafire/representation/metadata.hxx new file mode 100644 index 0000000..008ac25 --- /dev/null +++ b/code/seafire/representation/metadata.hxx @@ -0,0 +1,64 @@ +#ifndef code__seafire__representation__metadata_hxx_ +#define code__seafire__representation__metadata_hxx_ + +#include +#include + +#include +#include +#include + +#include + +#include +#include + +namespace code::seafire::representation +{ + + // etag + // + template + std::optional + get_etag(BR const& rep) + { + using local_traits = traits::representation_traits
; + + if constexpr (local_traits::has_entity_tag_t) + return rep.etag(); + + return std::nullopt; + } + + inline + std::optional + get_etag(representation_t const& rep) + { + return rep.etag(); + } + + + // last-modified + // + template + std::optional + get_last_modified(BR const& rep) + { + using local_traits = traits::representation_traits
; + + if constexpr (local_traits::has_last_modified) + return rep.last_modified(); + + return std::nullopt; + } + + inline + std::optional + get_last_modified(representation_t const& rep) + { + return rep.last_modified(); + } + +} // namespace code::seafire::representation + +#endif diff --git a/code/seafire/representation/negotiate.hxx b/code/seafire/representation/negotiate.hxx new file mode 100644 index 0000000..d54389f --- /dev/null +++ b/code/seafire/representation/negotiate.hxx @@ -0,0 +1,29 @@ +#ifndef code__seafire__representation__negotiate_hxx_ +#define code__seafire__representation__negotiate_hxx_ + +#include +#include +#include +#include +#include +#include + +#include + +namespace code::seafire::representation +{ + + // fixme: implement concept for handler. + // + + template + void + negotiate(server::request_t& req, + server::response_t& res, + Handler&& handler); + +} // namespace code::seafire::representation + +#include + +#endif diff --git a/code/seafire/representation/negotiate.txx b/code/seafire/representation/negotiate.txx new file mode 100644 index 0000000..b039636 --- /dev/null +++ b/code/seafire/representation/negotiate.txx @@ -0,0 +1,33 @@ +namespace code::seafire::representation +{ + + template + void + negotiate(server::request_t& req, + server::response_t& res, + Handler&& handler) + { + using t = traits::representation_traits; + + if constexpr (t::is_content_negotiable) { + using protocol::rfc7231::accept_t; + + if (auto opt_accept = get(req); opt_accept) { + auto const accept = *opt_accept; + + for (auto const& accepted_type : accept) { + if (R::is_accepted(accepted_type)) { + handler(accepted_type); + return; + } + } + + res.send(server::common_error_t::not_acceptable); + return; + } + } + + handler(std::nullopt); + } + +} // namespace code::seafire::representation diff --git a/code/seafire/representation/representation.cxx b/code/seafire/representation/representation.cxx new file mode 100644 index 0000000..78d7f6d --- /dev/null +++ b/code/seafire/representation/representation.cxx @@ -0,0 +1,38 @@ +#include + +namespace code::seafire::representation +{ + + protocol::media_type_t + representation_t:: + type() const + { + return r_->type(); + } + + std::optional + representation_t:: + etag() const + { + return r_->etag(); + } + + std::optional + representation_t:: + last_modified() const + { + return r_->last_modified(); + } + + protocol::media_type_t + representation_t:: + write_to(std::ostream& o) const + { + r_->write_to(o); + return r_->type(); + } + + representation_t::concept_t:: + ~concept_t() = default; + +} // namespace code::seafire::representation diff --git a/code/seafire/representation/representation.hxx b/code/seafire/representation/representation.hxx new file mode 100644 index 0000000..7994149 --- /dev/null +++ b/code/seafire/representation/representation.hxx @@ -0,0 +1,101 @@ +#ifndef code__seafire__representation__representation_hxx_ +#define code__seafire__representation__representation_hxx_ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace code::seafire::representation +{ + + class representation_t + { + public: + template + representation_t(R r); + + protocol::media_type_t + type() const; + + std::optional + etag() const; + + std::optional + last_modified() const; + + protocol::media_type_t + write_to(std::ostream& o) const; + + private: + struct concept_t; + + template + struct container_t; + + std::shared_ptr r_; + }; + + struct representation_t::concept_t + { + virtual + ~concept_t(); + + virtual + protocol::media_type_t + type() const = 0; + + virtual + std::optional + etag() const = 0; + + virtual + std::optional + last_modified() const = 0; + + virtual + void + write_to(std::ostream& o) const = 0; + + }; + + template + struct representation_t::container_t + : representation_t::concept_t + { + using representation_type = R; + using representation_traits = traits::representation_traits; + + container_t(representation_type r); + + protocol::media_type_t + type() const override; + + std::optional + etag() const override; + + std::optional + last_modified() const override; + + void + write_to(std::ostream& o) const override; + + representation_type r; + + }; + +} // namespace code::seafire::representation + +#include + +#endif diff --git a/code/seafire/representation/representation.txx b/code/seafire/representation/representation.txx new file mode 100644 index 0000000..7ec11d3 --- /dev/null +++ b/code/seafire/representation/representation.txx @@ -0,0 +1,54 @@ +namespace code::seafire::representation +{ + + template + representation_t:: + representation_t(R r) + : r_{std::make_shared>(std::move(r))} + {} + + template + representation_t::container_t:: + container_t(representation_type r) + : r{std::move(r)} + {} + + template + protocol::media_type_t + representation_t::container_t:: + type() const + { + return r.type(); + } + + template + std::optional + representation_t::container_t:: + etag() const + { + if constexpr (representation_traits::has_entity_tag) + return r.etag(); + + return {}; + } + + template + std::optional + representation_t::container_t:: + last_modified() const + { + if constexpr (representation_traits::has_last_modified) + return r.last_modified(); + + return {}; + } + + template + void + representation_t::container_t:: + write_to(std::ostream& o) const + { + r.write_to(o); + } + +} // namespace code::seafire::representation diff --git a/code/seafire/representation/select.hxx b/code/seafire/representation/select.hxx new file mode 100644 index 0000000..fd5228e --- /dev/null +++ b/code/seafire/representation/select.hxx @@ -0,0 +1,23 @@ +#ifndef code__seafire__representation__select_hxx_ +#define code__seafire__representation__select_hxx_ + +#include + +#include +#include +#include + +#include + +namespace code::seafire::representation +{ + + template + representation_t + select(R const&, std::optional const&); + +} // namespace code::seafire::representation + +#include + +#endif diff --git a/code/seafire/representation/select.txx b/code/seafire/representation/select.txx new file mode 100644 index 0000000..8ad775f --- /dev/null +++ b/code/seafire/representation/select.txx @@ -0,0 +1,22 @@ +namespace code::seafire::representation +{ + + template + representation_t + select(R const& rep, std::optional const& accepted_type) + { + using representation_traits = traits::representation_traits; + + static constexpr bool is_content_negotiable{ + representation_traits::is_content_negotiable + }; + + if constexpr (is_content_negotiable) { + return rep.select(accepted_type); + } + else { + return rep; + } + } + +} // namespace code::seafire::representation diff --git a/code/seafire/representation/send.hxx b/code/seafire/representation/send.hxx new file mode 100644 index 0000000..f048055 --- /dev/null +++ b/code/seafire/representation/send.hxx @@ -0,0 +1,27 @@ +#ifndef code__seafire__representation__send_hxx_ +#define code__seafire__representation__send_hxx_ + +#include +#include + +#include + +#include +#include + +namespace code::seafire::representation +{ + + template + void + send(server::request_t&, + server::response_t&, + protocol::status_code_t, + R const&, + bool); + +} // namespace code::seafire::representation + +#include + +#endif diff --git a/code/seafire/representation/send.txx b/code/seafire/representation/send.txx new file mode 100644 index 0000000..d117370 --- /dev/null +++ b/code/seafire/representation/send.txx @@ -0,0 +1,36 @@ +namespace code::seafire::representation +{ + + template + void + send(server::request_t& req, + server::response_t& res, + protocol::status_code_t status, + R const& rep, + bool send_content) + { + namespace rfc7231 = protocol::rfc7231; + namespace rfc7232 = protocol::rfc7232; + + set(res, rep.type()); + + if (auto etag = get_etag(rep); etag) { + set(res, *etag); + } + + if (auto last_modified = get_last_modified(rep); last_modified) { + set(res, *last_modified); + } + + if (!send_content) { + res.send(status); + return; + } + + auto content_stream = res.allocate_stream(); + rep.write_to(content_stream); + + res.send(status, content_stream); + } + +} // namespace code::seafire::representation diff --git a/code/seafire/representation/traits.hxx b/code/seafire/representation/traits.hxx new file mode 100644 index 0000000..8b0bd78 --- /dev/null +++ b/code/seafire/representation/traits.hxx @@ -0,0 +1,288 @@ +#ifndef code__seafire__representation__traits_hxx_ +#define code__seafire__representation__traits_hxx_ + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace code::seafire::representation::traits +{ + + // has_entity_tag + // + + template> + struct has_entity_tag + : std::false_type + {}; + + template + struct has_entity_tag< + R, + std::void_t< + decltype( + protocol::rfc7232::entity_tag_t{ + std::declval< + common::traits::remove_optional_t().etag())> + >() + } + ) + > + > : std::true_type + {}; + + template + constexpr bool has_entity_tag_v{has_entity_tag::value}; + + // has_last_modified + // + + template> + struct has_last_modified + : std::false_type + {}; + + template + struct has_last_modified< + R, + std::void_t< + decltype( + std::chrono::system_clock::time_point{ + std::declval< + common::traits::remove_optional_t().last_modified())> + >() + } + ) + > + > : std::true_type + {}; + + template + constexpr bool has_last_modified_v{has_last_modified::value}; + + + // ============================================================================= + // input representation. + // + + template< + typename R, + typename = std::void_t<> + > + struct is_input_representation + : std::false_type + {}; + + template + struct is_input_representation< + M, + std::void_t< + decltype( + bool{ + M::can_accept_input( + std::declval() + ) + } + ), + decltype( + M{ + std::declval< + decltype( + M::read_from( + std::declval>(), + std::declval() + ) + ) + >() + } + ) + > + > : std::true_type { + }; + + template + struct is_input_representation< + std::variant + > : std::bool_constant< + (is_input_representation::value && ...) + > { + }; + + template + constexpr bool is_input_representation_v{is_input_representation::value}; + + template + struct is_variant_input_representation : std::false_type {}; + + template + struct is_variant_input_representation< + std::variant + > : is_input_representation> { + }; + + template + constexpr bool is_variant_input_representation_v{is_variant_input_representation::value}; + + template + struct input_representation_traits + { + // TODO: DO. + }; + + + // ============================================================================= + // (output) representation. + // + + // is_basic_representation + // + + template< + typename R, + typename = std::void_t<> + > + struct is_basic_representation + : std::false_type + {}; + + template + struct is_basic_representation< + R, + std::void_t< + decltype( + protocol::media_type_t{ + std::declval().type() + } + ), + decltype( + std::declval().write_to(std::declval()) + ) + > + > : std::true_type + {}; + + template + constexpr bool is_basic_representation_v{ + is_basic_representation::value + }; + + // is_content_negotiable_representation + // + + template> + struct is_content_negotiable_representation + : std::false_type + {}; + + template + struct is_content_negotiable_representation< + R, + std::void_t< + // check that r::is_accepted(protocol::media_type_t) is valid. + // + decltype( + bool{ + R::is_accepted( + std::declval>() + ) + } + ), + // check that r{}.select(protocol::media_type_t) is valid. + // + decltype( + std::declval().select( + std::declval>() + ) + ) + > + > : std::bool_constant< + is_basic_representation_v< + // check that the type of the return value of r{}.select(...) is a representation. + // + decltype( + std::declval().select( + std::declval>() + ) + ) + > + > + {}; + + template + constexpr bool is_content_negotiable_representation_v{ + is_content_negotiable_representation::value + }; + + // is_representation + // + + template + struct is_representation : std::bool_constant< + is_basic_representation_v + || is_content_negotiable_representation_v + > + {}; + + template + constexpr bool is_representation_v{is_representation::value}; + + template + struct is_optional_representation + : std::false_type + {}; + + template + struct is_optional_representation> : std::bool_constant< + is_basic_representation_v + || is_content_negotiable_representation_v + > + {}; + + // is_optional_representation + // + + template + constexpr bool is_optional_representation_v{is_optional_representation::value}; + + // representation_traits + // + + template + struct representation_traits + { + using representation_type = common::traits::remove_optional_t; + + static constexpr bool is_optional{ + // Using R here since optional<> is removed from representation_type. + is_optional_representation_v + }; + + static constexpr bool is_basic { + is_basic_representation_v + }; + + static constexpr bool is_content_negotiable { + is_content_negotiable_representation_v + }; + + static constexpr bool has_entity_tag{ + has_entity_tag_v + }; + + static constexpr bool has_last_modified{ + has_last_modified_v + }; + }; + +} // namespace code::seafire::representation::traits + +#endif diff --git a/code/seafire/representation/version.hxx.in b/code/seafire/representation/version.hxx.in new file mode 100644 index 0000000..d8e59f6 --- /dev/null +++ b/code/seafire/representation/version.hxx.in @@ -0,0 +1,37 @@ +#ifndef code__seafire__representation__version_hxx_ +#define code__seafire__representation__version_hxx_ + +// The numeric version format is AAAAABBBBBCCCCCDDDE where: +// +// AAAAA - major version number +// BBBBB - minor version number +// CCCCC - bugfix version number +// DDD - alpha / beta (DDD + 500) version number +// E - final (0) / snapshot (1) +// +// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example: +// +// Version AAAAABBBBBCCCCCDDDE +// +// 0.1.0 0000000001000000000 +// 0.1.2 0000000001000020000 +// 1.2.3 0000100002000030000 +// 2.2.0-a.1 0000200001999990010 +// 3.0.0-b.2 0000299999999995020 +// 2.2.0-a.1.z 0000200001999990011 +// +#define LIBCODE_SEAFIRE_REPRESENTATION_VERSION $libcode_seafire_representation.version.project_number$ULL +#define LIBCODE_SEAFIRE_REPRESENTATION_VERSION_STR "$libcode_seafire_representation.version.project$" +#define LIBCODE_SEAFIRE_REPRESENTATION_VERSION_ID "$libcode_seafire_representation.version.project_id$" +#define LIBCODE_SEAFIRE_REPRESENTATION_VERSION_FULL "$libcode_seafire_representation.version$" + +#define LIBCODE_SEAFIRE_REPRESENTATION_VERSION_MAJOR $libcode_seafire_representation.version.major$ +#define LIBCODE_SEAFIRE_REPRESENTATION_VERSION_MINOR $libcode_seafire_representation.version.minor$ +#define LIBCODE_SEAFIRE_REPRESENTATION_VERSION_PATCH $libcode_seafire_representation.version.patch$ + +#define LIBCODE_SEAFIRE_REPRESENTATION_PRE_RELEASE $libcode_seafire_representation.version.pre_release$ + +#define LIBCODE_SEAFIRE_REPRESENTATION_SNAPSHOT_SN $libcode_seafire_representation.version.snapshot_sn$ULL +#define LIBCODE_SEAFIRE_REPRESENTATION_SNAPSHOT_ID "$libcode_seafire_representation.version.snapshot_id$" + +#endif diff --git a/manifest b/manifest new file mode 100644 index 0000000..15456bf --- /dev/null +++ b/manifest @@ -0,0 +1,15 @@ +: 1 +name: libcode-seafire-representation +version: 0.1.0-a.0.z +language: c++ +summary: libcode-seafire-representation C++ library +license: BSD-4-Clause +description-file: README.md +url: https://helloryan.se/code/ +email: ryan@helloryan.se +depends: * build2 >= 0.17.0 +depends: * bpkg >= 0.17.0 +depends: libasio ^1.29.0 +depends: libcode-seafire-common ^0.1.0- +depends: libcode-seafire-protocol ^0.1.0- +depends: libcode-seafire-server ^0.1.0- diff --git a/repositories.manifest b/repositories.manifest new file mode 100644 index 0000000..3ff25a8 --- /dev/null +++ b/repositories.manifest @@ -0,0 +1,19 @@ +: 1 +summary: libcode-seafire-representation project repository + +: +role: prerequisite +location: https://pkg.cppget.org/1/beta +trust: 70:64:FE:E4:E0:F3:60:F1:B4:51:E1:FA:12:5C:E0:B3:DB:DF:96:33:39:B9:2E:E5:C2:68:63:4C:A6:47:39:43 + +: +role: prerequisite +location: https://code.helloryan.se/code/libcode-seafire-common.git##HEAD + +: +role: prerequisite +location: https://code.helloryan.se/code/libcode-seafire-protocol.git##HEAD + +: +role: prerequisite +location: https://code.helloryan.se/code/libcode-seafire-server.git##HEAD diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..662178d --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,8 @@ +# Test executables. +# +driver + +# Testscript output directories (can be symlinks). +# +test +test-* diff --git a/tests/build/.gitignore b/tests/build/.gitignore new file mode 100644 index 0000000..974e01d --- /dev/null +++ b/tests/build/.gitignore @@ -0,0 +1,4 @@ +/config.build +/root/ +/bootstrap/ +build/ diff --git a/tests/build/bootstrap.build b/tests/build/bootstrap.build new file mode 100644 index 0000000..a07b5ea --- /dev/null +++ b/tests/build/bootstrap.build @@ -0,0 +1,5 @@ +project = # Unnamed tests subproject. + +using config +using test +using dist diff --git a/tests/build/root.build b/tests/build/root.build new file mode 100644 index 0000000..a67b2fe --- /dev/null +++ b/tests/build/root.build @@ -0,0 +1,16 @@ +cxx.std = latest + +using cxx + +hxx{*}: extension = hxx +ixx{*}: extension = ixx +txx{*}: extension = txx +cxx{*}: extension = cxx + +# Every exe{} in this subproject is by default a test. +# +exe{*}: test = true + +# The test target for cross-testing (running tests under Wine, etc). +# +test.target = $cxx.target diff --git a/tests/buildfile b/tests/buildfile new file mode 100644 index 0000000..aeeab15 --- /dev/null +++ b/tests/buildfile @@ -0,0 +1 @@ +./: {*/ -build/}