Hello libcode-seafire-resources
This commit is contained in:
commit
38c7f8edc9
17
.editorconfig
Normal file
17
.editorconfig
Normal file
@ -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
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
* text=auto
|
24
.gitea/workflows/on-push.yaml
Normal file
24
.gitea/workflows/on-push.yaml
Normal file
@ -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
|
31
.gitignore
vendored
Normal file
31
.gitignore
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
.bdep/
|
||||
|
||||
# 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
|
31
LICENSE
Normal file
31
LICENSE
Normal file
@ -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.
|
21
README.md
Normal file
21
README.md
Normal file
@ -0,0 +1,21 @@
|
||||
# libcode-seafire-resources
|
||||
|
||||
![Build status](https://code.helloryan.se/code/libcode-seafire-resources/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-resources repository.
|
4
build/.gitignore
vendored
Normal file
4
build/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/config.build
|
||||
/root/
|
||||
/bootstrap/
|
||||
build/
|
7
build/bootstrap.build
Normal file
7
build/bootstrap.build
Normal file
@ -0,0 +1,7 @@
|
||||
project = libcode-seafire-resources
|
||||
|
||||
using version
|
||||
using config
|
||||
using test
|
||||
using install
|
||||
using dist
|
6
build/export.build
Normal file
6
build/export.build
Normal file
@ -0,0 +1,6 @@
|
||||
$out_root/
|
||||
{
|
||||
include code/seafire/resources/
|
||||
}
|
||||
|
||||
export $out_root/code/seafire/resources/$import.target
|
16
build/root.build
Normal file
16
build/root.build
Normal file
@ -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
|
5
buildfile
Normal file
5
buildfile
Normal file
@ -0,0 +1,5 @@
|
||||
./: {code/ tests/} doc{README.md} legal{LICENSE} manifest
|
||||
|
||||
# Don't install tests.
|
||||
#
|
||||
tests/: install = false
|
9
code/seafire/resources/.gitignore
vendored
Normal file
9
code/seafire/resources/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# Generated version header.
|
||||
#
|
||||
version.hxx
|
||||
|
||||
# Unit test executables and Testscript output directories
|
||||
# (can be symlinks).
|
||||
#
|
||||
*.test
|
||||
test-*.test
|
67
code/seafire/resources/buildfile
Normal file
67
code/seafire/resources/buildfile
Normal file
@ -0,0 +1,67 @@
|
||||
intf_libs = # Interface dependencies.
|
||||
impl_libs = # Implementation dependencies.
|
||||
|
||||
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}
|
||||
import intf_libs =+ libcode-seafire-representation%lib{code-seafire-representation}
|
||||
|
||||
./: lib{code-seafire-resources}: libul{code-seafire-resources}
|
||||
|
||||
libul{code-seafire-resources}: {hxx ixx txx cxx}{** -**.test... -version} \
|
||||
{hxx }{ version}
|
||||
|
||||
libul{code-seafire-resources}: $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-resources}: 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-resources}:
|
||||
{
|
||||
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-resources}: bin.lib.version = "-$version.project_id"
|
||||
else
|
||||
lib{code-seafire-resources}: bin.lib.version = "-$version.major.$version.minor"
|
||||
|
||||
# Install into the code/seafire/resources/ subdirectory of, say, /usr/include/
|
||||
# recreating subdirectories.
|
||||
#
|
||||
{hxx ixx txx}{*}:
|
||||
{
|
||||
install = include/code/seafire/resources/
|
||||
install.subdirs = true
|
||||
}
|
33
code/seafire/resources/concepts.hxx
Normal file
33
code/seafire/resources/concepts.hxx
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef code__seafire__resources__concepts_hxx_
|
||||
#define code__seafire__resources__concepts_hxx_
|
||||
|
||||
#include <code/seafire/resources/traits.hxx>
|
||||
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
// resources.
|
||||
//
|
||||
|
||||
template<typename R>
|
||||
concept GettableResource = traits::is_gettable_resource_v<R>;
|
||||
|
||||
template<typename R>
|
||||
concept UpdatableResource = traits::is_updatable_resource_v<R>;
|
||||
|
||||
template<typename R>
|
||||
concept CreatableResource = traits::is_creatable_resource_v<R>;
|
||||
|
||||
template<typename R>
|
||||
concept ErasableResource = traits::is_erasable_resource_v<R>;
|
||||
|
||||
template<typename R>
|
||||
concept Resource
|
||||
= traits::is_gettable_resource_v<R>
|
||||
|| traits::is_updatable_resource_v<R>
|
||||
|| traits::is_creatable_resource_v<R>
|
||||
|| traits::is_erasable_resource_v<R>;
|
||||
|
||||
} // namespace code::seafire::resources
|
||||
|
||||
#endif
|
32
code/seafire/resources/handle-create.hxx
Normal file
32
code/seafire/resources/handle-create.hxx
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef code__seafire__resources__handle_create_hxx_
|
||||
#define code__seafire__resources__handle_create_hxx_
|
||||
|
||||
#include <code/seafire/common/invoke.hxx>
|
||||
#include <code/seafire/protocol/media-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/content-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/location.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/etag.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/last-modified.hxx>
|
||||
#include <code/seafire/representation/representation.hxx>
|
||||
#include <code/seafire/resources/concepts.hxx>
|
||||
#include <code/seafire/resources/handle-get.hxx>
|
||||
#include <code/seafire/resources/metadata.hxx>
|
||||
#include <code/seafire/resources/preconditions.hxx>
|
||||
#include <code/seafire/server/request.hxx>
|
||||
#include <code/seafire/server/response.hxx>
|
||||
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
template<CreatableResource R>
|
||||
void
|
||||
handle_create(server::request_t& req,
|
||||
server::response_t& res,
|
||||
R const& resource,
|
||||
std::optional<protocol::media_type_t> const& accepted_type);
|
||||
|
||||
} // namespace code::seafire::resources
|
||||
|
||||
#include <code/seafire/resources/handle-create.txx>
|
||||
|
||||
#endif
|
34
code/seafire/resources/handle-create.txx
Normal file
34
code/seafire/resources/handle-create.txx
Normal file
@ -0,0 +1,34 @@
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
template<CreatableResource R>
|
||||
void
|
||||
handle_create(server::request_t& req,
|
||||
server::response_t& res,
|
||||
R const& resource,
|
||||
std::optional<protocol::media_type_t> const& accepted_type)
|
||||
{
|
||||
using resource_traits = traits::resource_traits<std::decay_t<R>>;
|
||||
using input_representation_type = typename resource_traits::create_input_representation_type;
|
||||
|
||||
using t = traits::resource_traits<R>;
|
||||
|
||||
if constexpr (representation::traits::is_variant_input_representation_v<input_representation_type>) {
|
||||
res.send(server::common_error_t::not_implemented);
|
||||
}
|
||||
else if constexpr (representation::Representation<typename t::create_result_type>) {
|
||||
auto content_type = get<protocol::rfc7231::content_type_t>(req);
|
||||
auto input = input_representation_type::read_from(content_type, req.content());
|
||||
auto result = common::invoke(resource, req, &R::create, input);
|
||||
auto selected_rep = representation::select(result, accepted_type);
|
||||
representation::send(req, res, 201, selected_rep, true);
|
||||
}
|
||||
else {
|
||||
auto content_type = get<protocol::rfc7231::content_type_t>(req);
|
||||
auto input = input_representation_type::read_from(content_type, req.content());
|
||||
auto result = common::invoke(resource, req, &R::create, input);
|
||||
handle_get(req, res, result, accepted_type, get_kind_t::created_resource);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace code::seafire::resources
|
42
code/seafire/resources/handle-get.hxx
Normal file
42
code/seafire/resources/handle-get.hxx
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef code__seafire__resources__handle_get_hxx_
|
||||
#define code__seafire__resources__handle_get_hxx_
|
||||
|
||||
#include <code/seafire/common/invoke.hxx>
|
||||
#include <code/seafire/protocol/media-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/content-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/location.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/etag.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/last-modified.hxx>
|
||||
#include <code/seafire/representation/representation.hxx>
|
||||
#include <code/seafire/representation/select.hxx>
|
||||
#include <code/seafire/representation/send.hxx>
|
||||
#include <code/seafire/resources/concepts.hxx>
|
||||
#include <code/seafire/resources/metadata.hxx>
|
||||
#include <code/seafire/resources/preconditions.hxx>
|
||||
#include <code/seafire/server/transaction.hxx>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
enum class get_kind_t {
|
||||
get,
|
||||
head,
|
||||
created_resource,
|
||||
updated_resource
|
||||
};
|
||||
|
||||
template<GettableResource R>
|
||||
void
|
||||
handle_get(server::request_t& req,
|
||||
server::response_t& res,
|
||||
R const& resource,
|
||||
std::optional<protocol::media_type_t> const& accepted_type,
|
||||
get_kind_t kind);
|
||||
|
||||
} // namespace code::seafire::resources
|
||||
|
||||
#include <code/seafire/resources/handle-get.txx>
|
||||
|
||||
#endif
|
47
code/seafire/resources/handle-get.txx
Normal file
47
code/seafire/resources/handle-get.txx
Normal file
@ -0,0 +1,47 @@
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
template<GettableResource R>
|
||||
void
|
||||
handle_get(server::request_t& req,
|
||||
server::response_t& res,
|
||||
R const& resource,
|
||||
std::optional<protocol::media_type_t> const& accepted_type,
|
||||
get_kind_t kind)
|
||||
{
|
||||
auto rep = common::invoke(resource, req, &R::get);
|
||||
|
||||
if (!rep) {
|
||||
res.send(server::common_error_t::not_found);
|
||||
return;
|
||||
}
|
||||
|
||||
auto selected_rep = representation::select(*rep, accepted_type);
|
||||
|
||||
if (!check_preconditions(req, res, selected_rep))
|
||||
return;
|
||||
|
||||
namespace rfc7231 = protocol::rfc7231;
|
||||
namespace rfc7232 = protocol::rfc7232;
|
||||
|
||||
if (auto opt_val = get_etag(resource); opt_val)
|
||||
set<rfc7232::etag_t>(res, *opt_val);
|
||||
|
||||
if (auto opt_val = get_last_modified(resource); opt_val)
|
||||
set<rfc7232::last_modified_t>(res, *opt_val);
|
||||
|
||||
if (kind == get_kind_t::created_resource || kind == get_kind_t::updated_resource) {
|
||||
if (auto opt_val = get_location(resource); opt_val)
|
||||
set<rfc7231::location_t>(res, *opt_val);
|
||||
}
|
||||
|
||||
protocol::status_code_t status{200};
|
||||
|
||||
if (kind == get_kind_t::created_resource)
|
||||
status = 201;
|
||||
|
||||
bool const send_content = kind != get_kind_t::head;
|
||||
representation::send(req, res, status, selected_rep, send_content);
|
||||
}
|
||||
|
||||
} // namespace code::seafire::resources
|
34
code/seafire/resources/handle-update.hxx
Normal file
34
code/seafire/resources/handle-update.hxx
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef code__seafire__resources__handle_update_hxx_
|
||||
#define code__seafire__resources__handle_update_hxx_
|
||||
|
||||
#include <code/seafire/common/invoke.hxx>
|
||||
#include <code/seafire/protocol/media-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/content-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/location.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/etag.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/last-modified.hxx>
|
||||
#include <code/seafire/representation/representation.hxx>
|
||||
#include <code/seafire/resources/concepts.hxx>
|
||||
#include <code/seafire/resources/handle-get.hxx>
|
||||
#include <code/seafire/resources/metadata.hxx>
|
||||
#include <code/seafire/resources/preconditions.hxx>
|
||||
#include <code/seafire/server/request.hxx>
|
||||
#include <code/seafire/server/response.hxx>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
template<UpdatableResource R>
|
||||
void
|
||||
handle_update(server::request_t& req,
|
||||
server::response_t& res,
|
||||
R const& resource,
|
||||
std::optional<protocol::media_type_t> const& accepted_type);
|
||||
|
||||
} // namespace code::seafire::resources
|
||||
|
||||
#include <code/seafire/resources/handle-update.txx>
|
||||
|
||||
#endif
|
45
code/seafire/resources/handle-update.txx
Normal file
45
code/seafire/resources/handle-update.txx
Normal file
@ -0,0 +1,45 @@
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
template<UpdatableResource R>
|
||||
void
|
||||
handle_update(server::request_t& req,
|
||||
server::response_t& res,
|
||||
R const& resource,
|
||||
std::optional<protocol::media_type_t> const& accepted_type)
|
||||
{
|
||||
using resource_traits = traits::resource_traits<std::decay_t<R>>;
|
||||
using input_representation_type = typename resource_traits::update_input_representation_type;
|
||||
|
||||
auto content_type = get<protocol::rfc7231::content_type_t>(req);
|
||||
|
||||
if (!content_type) {
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!input_representation_type::can_accept_input(*content_type)) {
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
auto input = input_representation_type::read_from(content_type, req.content());
|
||||
|
||||
using t = traits::resource_traits<R>;
|
||||
|
||||
if constexpr (GettableResource<typename t::update_result_type>) {
|
||||
auto result = common::invoke(resource, req, &R::update, input);
|
||||
handle_get(req, res, result, accepted_type, get_kind_t::updated_resource);
|
||||
}
|
||||
else if constexpr (representation::Representation<typename t::update_result_type>) {
|
||||
auto result = common::invoke(resource, req, &R::update, input);
|
||||
auto selected_rep = representation::select(result, accepted_type);
|
||||
representation::send(req, res, 200, selected_rep, true);
|
||||
}
|
||||
else {
|
||||
common::invoke(resource, req, &R::update, input);
|
||||
res.send(204);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace code::seafire::resources
|
29
code/seafire/resources/handle.hxx
Normal file
29
code/seafire/resources/handle.hxx
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef code__seafire__resources__handle_hxx_
|
||||
#define code__seafire__resources__handle_hxx_
|
||||
|
||||
#include <code/seafire/protocol/rfc7231/allow.hxx>
|
||||
#include <code/seafire/resources/concepts.hxx>
|
||||
#include <code/seafire/resources/handle-create.hxx>
|
||||
#include <code/seafire/resources/handle-get.hxx>
|
||||
#include <code/seafire/resources/handle-update.hxx>
|
||||
#include <code/seafire/resources/negotiate.hxx>
|
||||
#include <code/seafire/resources/traits.hxx>
|
||||
#include <code/seafire/server/request.hxx>
|
||||
#include <code/seafire/server/response.hxx>
|
||||
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
void
|
||||
handle(server::request_t& req,
|
||||
server::response_t& res,
|
||||
Resource auto const& r);
|
||||
|
||||
} // namespace code::seafire::resources
|
||||
|
||||
#include <code/seafire/resources/handle.txx>
|
||||
|
||||
#endif
|
129
code/seafire/resources/handle.txx
Normal file
129
code/seafire/resources/handle.txx
Normal file
@ -0,0 +1,129 @@
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
void
|
||||
handle(server::request_t& req,
|
||||
server::response_t& res,
|
||||
Resource auto const& r)
|
||||
{
|
||||
using resource_traits = traits::resource_traits<std::decay_t<decltype(r)>>;
|
||||
|
||||
auto const not_allowed = [&req, &res]
|
||||
{
|
||||
set<protocol::rfc7231::allow_t>(res, resource_traits::allowed_methods());
|
||||
res.send(server::common_error_t::method_not_allowed);
|
||||
};
|
||||
|
||||
auto const& method = req.get_message().method();
|
||||
|
||||
// GET
|
||||
//
|
||||
if (method == "GET" || method == "HEAD") {
|
||||
if constexpr (resource_traits::is_gettable) {
|
||||
negotiate<typename resource_traits::get_representation_type>(
|
||||
req,
|
||||
res,
|
||||
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
|
||||
{
|
||||
handle_get(
|
||||
req,
|
||||
res,
|
||||
r,
|
||||
accepted_type,
|
||||
req.get_message().method() == "HEAD" ? get_kind_t::head : get_kind_t::get
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
not_allowed();
|
||||
}
|
||||
}
|
||||
|
||||
// PUT (update)
|
||||
//
|
||||
else if (method == "PUT") {
|
||||
if constexpr (resource_traits::is_updatable) {
|
||||
if constexpr (representation::Representation<typename resource_traits::update_result_type>) {
|
||||
negotiate<typename resource_traits::update_result_type>(
|
||||
req,
|
||||
res,
|
||||
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
|
||||
{
|
||||
handle_update(req, res, r, accepted_type);
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
negotiate<typename traits::resource_traits<typename resource_traits::update_result_type>::get_representation_type>(
|
||||
req,
|
||||
res,
|
||||
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
|
||||
{
|
||||
handle_update(req, res, r, accepted_type);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
not_allowed();
|
||||
}
|
||||
}
|
||||
|
||||
// POST (create)
|
||||
//
|
||||
else if (method == "POST") {
|
||||
if constexpr (resource_traits::is_creatable) {
|
||||
if constexpr (representation::Representation<typename resource_traits::create_result_type>) {
|
||||
negotiate<typename resource_traits::create_result_type>(
|
||||
req,
|
||||
res,
|
||||
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
|
||||
{
|
||||
handle_create(req, res, r, accepted_type);
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
using one = typename resource_traits::create_result_type;
|
||||
using two = typename traits::resource_traits<one>::get_representation_type;
|
||||
//using resource_traits = traits::resource_traits<
|
||||
negotiate<two>(
|
||||
req,
|
||||
res,
|
||||
[&req, &res, &r](std::optional<protocol::media_type_t> const& accepted_type)
|
||||
{
|
||||
handle_create(req, res, r, accepted_type);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
not_allowed();
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE
|
||||
//
|
||||
else if (method == "DELETE") {
|
||||
#if 0
|
||||
if constexpr (resource_traits::is_erasable) {
|
||||
handle_erase(tx, r);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
not_allowed();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// method not implemented.
|
||||
//
|
||||
else {
|
||||
res.send(server::common_error_t::not_implemented);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace code::seafire::resources
|
104
code/seafire/resources/metadata.hxx
Normal file
104
code/seafire/resources/metadata.hxx
Normal file
@ -0,0 +1,104 @@
|
||||
#ifndef code__seafire__resources__metadata_hxx_
|
||||
#define code__seafire__resources__metadata_hxx_
|
||||
|
||||
#include <code/seafire/protocol/media-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/entity-tag.hxx>
|
||||
#include <code/seafire/resources/concepts.hxx>
|
||||
#include <code/seafire/resources/traits.hxx>
|
||||
|
||||
#include <code/seafire/representation/concepts.hxx>
|
||||
#include <code/seafire/representation/representation.hxx>
|
||||
#include <code/seafire/representation/traits.hxx>
|
||||
|
||||
#include <code/uri/uri.hxx>
|
||||
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
// location
|
||||
//
|
||||
template<Resource R>
|
||||
std::optional<uri::uri_t>
|
||||
get_location(R const& res)
|
||||
{
|
||||
using local_traits = traits::resource_traits<R>;
|
||||
|
||||
if constexpr (local_traits::has_location)
|
||||
return res.location();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
// etag
|
||||
//
|
||||
template<representation::BasicRepresentation BR>
|
||||
std::optional<protocol::rfc7232::entity_tag_t>
|
||||
get_etag(BR const& rep)
|
||||
{
|
||||
using local_traits = representation::traits::representation_traits<BR>;
|
||||
|
||||
if constexpr (local_traits::has_entity_tag_t)
|
||||
return rep.etag();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline
|
||||
std::optional<protocol::rfc7232::entity_tag_t>
|
||||
get_etag(representation::representation_t const& rep)
|
||||
{
|
||||
return rep.etag();
|
||||
}
|
||||
|
||||
template<Resource R>
|
||||
std::optional<protocol::rfc7232::entity_tag_t>
|
||||
get_etag(R const& res)
|
||||
{
|
||||
using local_traits = traits::resource_traits<R>;
|
||||
|
||||
if constexpr (local_traits::has_entity_tag)
|
||||
return res.etag();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// last-modified
|
||||
//
|
||||
template<representation::BasicRepresentation BR>
|
||||
std::optional<std::chrono::system_clock::time_point>
|
||||
get_last_modified(BR const& rep)
|
||||
{
|
||||
using local_traits = representation::traits::representation_traits<BR>;
|
||||
|
||||
if constexpr (local_traits::has_last_modified)
|
||||
return rep.last_modified();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline
|
||||
std::optional<std::chrono::system_clock::time_point>
|
||||
get_last_modified(representation::representation_t const& rep)
|
||||
{
|
||||
return rep.last_modified();
|
||||
}
|
||||
|
||||
template<Resource R>
|
||||
std::optional<std::chrono::system_clock::time_point>
|
||||
get_last_modified(R const& res)
|
||||
{
|
||||
using local_traits = traits::resource_traits<R>;
|
||||
|
||||
if constexpr (local_traits::has_last_modified)
|
||||
return res.last_modified();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace code::seafire::resources
|
||||
|
||||
#endif
|
28
code/seafire/resources/negotiate.hxx
Normal file
28
code/seafire/resources/negotiate.hxx
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef code__seafire__resources__negotiate_hxx_
|
||||
#define code__seafire__resources__negotiate_hxx_
|
||||
|
||||
#include <code/seafire/protocol/media-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/accept.hxx>
|
||||
#include <code/seafire/representation/concepts.hxx>
|
||||
#include <code/seafire/representation/traits.hxx>
|
||||
#include <code/seafire/resources/concepts.hxx>
|
||||
#include <code/seafire/resources/traits.hxx>
|
||||
#include <code/seafire/server/request.hxx>
|
||||
#include <code/seafire/server/response.hxx>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
// fixme: implement concept for handler.
|
||||
|
||||
template<representation::Representation R, typename Handler>
|
||||
void
|
||||
negotiate(server::request_t&, server::response_t&, Handler&&);
|
||||
|
||||
} // namespace code::seafire::resources
|
||||
|
||||
#include <code/seafire/resources/negotiate.txx>
|
||||
|
||||
#endif
|
31
code/seafire/resources/negotiate.txx
Normal file
31
code/seafire/resources/negotiate.txx
Normal file
@ -0,0 +1,31 @@
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
template<representation::Representation R, typename Handler>
|
||||
void
|
||||
negotiate(server::request_t& req, server::response_t& res, Handler&& handler)
|
||||
{
|
||||
using representation_traits = representation::traits::representation_traits<R>;
|
||||
|
||||
if constexpr (representation_traits::is_content_negotiable) {
|
||||
using protocol::rfc7231::accept_t;
|
||||
|
||||
if (auto opt_accept = get<accept_t>(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::resources
|
67
code/seafire/resources/preconditions.hxx
Normal file
67
code/seafire/resources/preconditions.hxx
Normal file
@ -0,0 +1,67 @@
|
||||
#ifndef code__seafire__resources__preconditions_hxx_
|
||||
#define code__seafire__resources__preconditions_hxx_
|
||||
|
||||
#include <code/seafire/protocol/rfc7232/if-match.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/if-modified-since.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/if-none-match.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/if-unmodified-since.hxx>
|
||||
#include <code/seafire/resources/concepts.hxx>
|
||||
#include <code/seafire/resources/traits.hxx>
|
||||
#include <code/seafire/server/request.hxx>
|
||||
#include <code/seafire/server/response.hxx>
|
||||
|
||||
#include <code/seafire/representation/concepts.hxx>
|
||||
#include <code/seafire/representation/traits.hxx>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
bool
|
||||
check_preconditions(
|
||||
server::request_t& req,
|
||||
server::response_t& res,
|
||||
representation::BasicRepresentation auto const& r);
|
||||
|
||||
// If-Match
|
||||
//
|
||||
bool
|
||||
check_if_match(
|
||||
server::request_t& req,
|
||||
server::response_t& res,
|
||||
representation::BasicRepresentation auto const& r,
|
||||
protocol::rfc7232::if_match_t const& if_match);
|
||||
|
||||
// If-Unmodified-Since
|
||||
//
|
||||
bool
|
||||
check_if_unmodified_since(
|
||||
server::request_t& req,
|
||||
server::response_t& res,
|
||||
representation::BasicRepresentation auto const& representation,
|
||||
std::chrono::system_clock::time_point const& if_unmodified_since);
|
||||
|
||||
// If-None-Match
|
||||
//
|
||||
bool
|
||||
check_if_none_match(
|
||||
server::request_t& req,
|
||||
server::response_t& res,
|
||||
representation::BasicRepresentation auto const& r,
|
||||
protocol::rfc7232::if_none_match_t const& if_none_match);
|
||||
|
||||
// If-Modified-Since
|
||||
//
|
||||
bool
|
||||
check_if_modified_since(
|
||||
server::request_t& req,
|
||||
server::response_t& res,
|
||||
representation::BasicRepresentation auto const& r,
|
||||
std::chrono::system_clock::time_point const& if_modified_since);
|
||||
|
||||
} // namespace code::seafire::resources
|
||||
|
||||
#include <code/seafire/resources/preconditions.txx>
|
||||
|
||||
#endif
|
151
code/seafire/resources/preconditions.txx
Normal file
151
code/seafire/resources/preconditions.txx
Normal file
@ -0,0 +1,151 @@
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
bool
|
||||
check_preconditions(
|
||||
server::request_t& req,
|
||||
server::response_t& res,
|
||||
representation::BasicRepresentation auto const& r)
|
||||
{
|
||||
namespace rfc7232 = protocol::rfc7232;
|
||||
|
||||
using representation_traits = representation::traits::representation_traits<decltype(r)>;
|
||||
|
||||
if (auto if_match = get<rfc7232::if_match_t>(req); if_match) {
|
||||
if constexpr (representation_traits::has_entity_tag) {
|
||||
bool match = check_if_match(req, res, r, *if_match);
|
||||
|
||||
if (!match) {
|
||||
res.send(server::common_error_t::precondition_failed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
res.send(server::common_error_t::precondition_failed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (auto if_unmodified_since = get<rfc7232::if_unmodified_since_t>(req); if_unmodified_since) {
|
||||
bool match = check_if_unmodified_since(req, res, r, *if_unmodified_since);
|
||||
|
||||
if (!match) {
|
||||
res.send(server::common_error_t::precondition_failed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto if_none_match = get<rfc7232::if_none_match_t>(req); if_none_match) {
|
||||
bool match = false;
|
||||
|
||||
if (!match) {
|
||||
if (req.get_message().method() == "GET" || req.get_message().method() == "HEAD")
|
||||
res.send(server::common_error_t::not_modified);
|
||||
else
|
||||
res.send(server::common_error_t::precondition_failed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (auto if_modified_since = get<rfc7232::if_modified_since_t>(req); if_modified_since) {
|
||||
bool match = false;
|
||||
|
||||
if (!match) {
|
||||
res.send(server::common_error_t::precondition_failed);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// If-Match
|
||||
//
|
||||
bool
|
||||
check_if_match(
|
||||
server::request_t& req,
|
||||
server::response_t& res,
|
||||
representation::BasicRepresentation auto const& r,
|
||||
protocol::rfc7232::if_match_t const& if_match)
|
||||
{
|
||||
using representation_traits = representation::traits::representation_traits<std::decay_t<decltype(r)>>;
|
||||
|
||||
if constexpr (representation_traits::has_entity_tag) {
|
||||
auto etag = [&r]
|
||||
{
|
||||
if constexpr (common::traits::is_optional_v<decltype(r.etag())>) {
|
||||
return r.etag();
|
||||
}
|
||||
else {
|
||||
return std::optional<protocol::rfc7232::entity_tag_t>{r.etag()};
|
||||
}
|
||||
}();
|
||||
|
||||
if (!etag)
|
||||
return false;
|
||||
|
||||
if (if_match.is_anything())
|
||||
return true;
|
||||
|
||||
for (auto const& j : if_match.tags()) {
|
||||
if (strong_compare(*etag, j))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// If-Unmodified-Since
|
||||
//
|
||||
bool
|
||||
check_if_unmodified_since(
|
||||
server::request_t& req,
|
||||
server::response_t& res,
|
||||
representation::BasicRepresentation auto const& r,
|
||||
std::chrono::system_clock::time_point const& if_unmodified_since)
|
||||
{
|
||||
using representation_traits = representation::traits::representation_traits<std::decay_t<decltype(r)>>;
|
||||
|
||||
if constexpr (representation_traits::has_last_modified) {
|
||||
auto last_modified = r.last_modified();
|
||||
|
||||
if (last_modified <= if_unmodified_since)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// If-None-Match
|
||||
//
|
||||
bool
|
||||
check_if_none_match(
|
||||
server::request_t& req,
|
||||
server::response_t& res,
|
||||
representation::BasicRepresentation auto const& r,
|
||||
protocol::rfc7232::if_none_match_t const& if_none_match)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If-Modified-Since
|
||||
//
|
||||
bool
|
||||
check_if_modified_since(
|
||||
server::request_t& req,
|
||||
server::response_t& res,
|
||||
representation::BasicRepresentation auto const& r,
|
||||
std::chrono::system_clock::time_point const& if_modified_since)
|
||||
{
|
||||
using representation_traits = representation::traits::representation_traits<std::decay_t<decltype(r)>>;
|
||||
|
||||
if constexpr (representation_traits::has_last_modified) {
|
||||
auto last_modified = r.last_modified();
|
||||
|
||||
if (last_modified >= if_modified_since)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace code::seafire::resources
|
66
code/seafire/resources/resource-handler.hxx
Normal file
66
code/seafire/resources/resource-handler.hxx
Normal file
@ -0,0 +1,66 @@
|
||||
#ifndef code__seafire__resources__resource_handler_hxx_
|
||||
#define code__seafire__resources__resource_handler_hxx_
|
||||
|
||||
#include <code/seafire/common/invoke.hxx>
|
||||
#include <code/seafire/protocol/media-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/accept.hxx>
|
||||
#include <code/seafire/protocol/rfc7231/content-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/entity-tag.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/etag.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/last-modified.hxx>
|
||||
#include <code/seafire/resources/concepts.hxx>
|
||||
#include <code/seafire/resources/handle.hxx>
|
||||
#include <code/seafire/resources/traits.hxx>
|
||||
#include <code/seafire/server/request.hxx>
|
||||
#include <code/seafire/server/response.hxx>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
template<Resource R,
|
||||
typename F, // TODO: Implement factory concept
|
||||
typename... FactoryArgs>
|
||||
class resource_handler_t {
|
||||
public:
|
||||
using resource_type = R;
|
||||
using factory_type = F;
|
||||
|
||||
explicit
|
||||
resource_handler_t(factory_type factory, FactoryArgs&&... args);
|
||||
|
||||
factory_type const&
|
||||
factory() const
|
||||
{
|
||||
return factory_;
|
||||
}
|
||||
|
||||
void
|
||||
on_request(server::request_t& req, server::response_t& res) const;
|
||||
|
||||
void
|
||||
operator()(server::request_t& req, server::response_t& res) const
|
||||
{
|
||||
on_request(req, res);
|
||||
}
|
||||
|
||||
private:
|
||||
factory_type factory_;
|
||||
std::tuple<FactoryArgs...> factory_args_;
|
||||
};
|
||||
|
||||
template<Resource R,
|
||||
typename F,
|
||||
typename... FactoryArgs,
|
||||
typename H = resource_handler_t<R, F, FactoryArgs...>>
|
||||
H use_resource(F factory, FactoryArgs&&... args);
|
||||
|
||||
} // namespace code::seafire::resources
|
||||
|
||||
#include <code/seafire/resources/resource-handler.txx>
|
||||
|
||||
#endif
|
36
code/seafire/resources/resource-handler.txx
Normal file
36
code/seafire/resources/resource-handler.txx
Normal file
@ -0,0 +1,36 @@
|
||||
namespace code::seafire::resources
|
||||
{
|
||||
|
||||
template<Resource R, typename F, typename... FactoryArgs >
|
||||
resource_handler_t<R, F, FactoryArgs...>::
|
||||
resource_handler_t(factory_type factory, FactoryArgs&&... args)
|
||||
: factory_{std::move(factory)},
|
||||
factory_args_{std::forward<FactoryArgs>(args)...}
|
||||
{}
|
||||
|
||||
template< Resource R, typename F, typename... FactoryArgs >
|
||||
void
|
||||
resource_handler_t< R, F, FactoryArgs... >::
|
||||
on_request(server::request_t& req,
|
||||
server::response_t& res) const
|
||||
{
|
||||
auto factory_invoker = [f = factory(), &req, &res](auto... args)
|
||||
{
|
||||
return common::invoke(req, &F::template factory<resource_type>::make, f, req, res, std::forward<decltype(args)>(args)...);
|
||||
};
|
||||
|
||||
auto r = std::apply(factory_invoker, factory_args_);
|
||||
handle(req, res, r);
|
||||
}
|
||||
|
||||
template<Resource R,
|
||||
typename F,
|
||||
typename... FactoryArgs,
|
||||
typename H>
|
||||
H
|
||||
use_resource(F factory, FactoryArgs&&... args)
|
||||
{
|
||||
return H{std::move(factory), std::forward<FactoryArgs>(args)...};
|
||||
}
|
||||
|
||||
} // namespace code::seafire::resources
|
316
code/seafire/resources/traits.hxx
Normal file
316
code/seafire/resources/traits.hxx
Normal file
@ -0,0 +1,316 @@
|
||||
#ifndef code__seafire__resources__traits_hxx_
|
||||
#define code__seafire__resources__traits_hxx_
|
||||
|
||||
#include <code/seafire/common/traits.hxx>
|
||||
#include <code/seafire/protocol/media-type.hxx>
|
||||
#include <code/seafire/protocol/rfc7232/entity-tag.hxx>
|
||||
#include <code/seafire/protocol/token.hxx>
|
||||
#include <code/seafire/representation/traits.hxx>
|
||||
|
||||
#include <code/uri/uri.hxx>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace code::seafire::resources::traits
|
||||
{
|
||||
|
||||
// =============================================================================
|
||||
// resources.
|
||||
//
|
||||
|
||||
// is_gettable_resource<R>
|
||||
//
|
||||
|
||||
template<typename, typename = std::void_t<>>
|
||||
struct is_gettable_resource : std::false_type {};
|
||||
|
||||
template<typename R>
|
||||
struct is_gettable_resource<
|
||||
R,
|
||||
std::void_t<
|
||||
decltype(&R::get)
|
||||
>
|
||||
> : std::bool_constant<
|
||||
std::is_same_v<
|
||||
void,
|
||||
typename common::traits::function_traits<decltype(&R::get)>::return_type
|
||||
>
|
||||
||
|
||||
representation::traits::is_optional_representation_v<
|
||||
typename common::traits::function_traits<decltype(&R::get)>::return_type
|
||||
>
|
||||
> {
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
inline constexpr bool is_gettable_resource_v{is_gettable_resource<R>::value};
|
||||
|
||||
template<typename R, bool = is_gettable_resource_v<R>>
|
||||
struct get_traits {
|
||||
using result_type = void;
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
struct get_traits<R, true> {
|
||||
using function_traits = common::traits::function_traits<decltype(&R::get)>;
|
||||
using result_type = common::traits::remove_optional_t<typename function_traits::return_type>;
|
||||
};
|
||||
|
||||
// is_updatable_resource<R>
|
||||
//
|
||||
|
||||
template<typename, typename = std::void_t<>>
|
||||
struct is_updatable_resource : std::false_type {};
|
||||
|
||||
template<typename R>
|
||||
struct is_updatable_resource<
|
||||
R,
|
||||
std::void_t<
|
||||
decltype(&R::update)
|
||||
>
|
||||
> : std::bool_constant<
|
||||
// Check if the first arg to R::update() is an input representation.
|
||||
representation::traits::is_input_representation_v<
|
||||
typename common::traits::first_arg<decltype(&R::update)>::type
|
||||
>
|
||||
&&
|
||||
(
|
||||
// Check if return value of R::update() is void.
|
||||
std::is_same_v<
|
||||
void,
|
||||
typename common::traits::function_traits<decltype(&R::update)>::return_type
|
||||
>
|
||||
||
|
||||
// Check if return value of R::update() is a gettable resource.
|
||||
is_gettable_resource_v<
|
||||
typename common::traits::function_traits<decltype(&R::update)>::return_type
|
||||
>
|
||||
||
|
||||
// Check if return value of R::update() is a representation.
|
||||
representation::traits::is_representation_v<
|
||||
typename common::traits::function_traits<decltype(&R::update)>::return_type
|
||||
>
|
||||
)
|
||||
> {
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
inline constexpr bool is_updatable_resource_v{is_updatable_resource<R>::value};
|
||||
|
||||
template<typename R, bool = is_updatable_resource_v<R>>
|
||||
struct update_traits {
|
||||
using input_representation_type = void;
|
||||
using result_type = void;
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
struct update_traits<R, true> {
|
||||
using function_traits = common::traits::function_traits<decltype(&R::update)>;
|
||||
using input_representation_type = common::traits::first_arg_t<decltype(&R::update)>;
|
||||
using result_type = typename function_traits::return_type;
|
||||
};
|
||||
|
||||
// is_creatable_resource<R>
|
||||
//
|
||||
|
||||
template<typename, typename = std::void_t<>>
|
||||
struct is_creatable_resource : std::false_type {};
|
||||
|
||||
template<typename R>
|
||||
struct is_creatable_resource<
|
||||
R,
|
||||
std::void_t<
|
||||
decltype(&R::create)
|
||||
>
|
||||
> : std::bool_constant<
|
||||
representation::traits::is_input_representation_v<
|
||||
common::traits::first_arg_t<decltype(&R::create)>
|
||||
>
|
||||
&&
|
||||
(
|
||||
// Check if return value of R::create() is void.
|
||||
std::is_same_v<
|
||||
void,
|
||||
typename common::traits::function_traits<decltype(&R::create)>::return_type
|
||||
>
|
||||
||
|
||||
// Check if return value of R::create() is a gettable resource.
|
||||
is_gettable_resource_v<
|
||||
typename common::traits::function_traits<decltype(&R::create)>::return_type
|
||||
>
|
||||
||
|
||||
// Check if return value of R::create() is a representation.
|
||||
representation::traits::is_representation_v<
|
||||
typename common::traits::function_traits<decltype(&R::create)>::return_type
|
||||
>
|
||||
)
|
||||
> {
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
inline constexpr bool is_creatable_resource_v{is_creatable_resource<R>::value};
|
||||
|
||||
template<typename R, bool = is_creatable_resource_v<R>>
|
||||
struct create_traits {
|
||||
using input_representation_type = void;
|
||||
using result_type = void;
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
struct create_traits<R, true> {
|
||||
using function_traits = common::traits::function_traits<decltype(&R::create)>;
|
||||
using input_representation_type = common::traits::first_arg_t<decltype(&R::create)>;
|
||||
using result_type = typename function_traits::return_type;
|
||||
};
|
||||
|
||||
// is_erasable_resource<R>
|
||||
//
|
||||
|
||||
template<typename, typename = std::void_t<>>
|
||||
struct is_erasable_resource : std::false_type {};
|
||||
|
||||
template<typename R>
|
||||
struct is_erasable_resource<
|
||||
R,
|
||||
std::void_t<
|
||||
decltype(std::declval<R const>().erase())
|
||||
>
|
||||
> : std::true_type{
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
constexpr bool is_erasable_resource_v{is_erasable_resource<R>::value};
|
||||
|
||||
// is_resource
|
||||
//
|
||||
|
||||
template<typename R>
|
||||
struct is_resource : std::bool_constant<
|
||||
is_gettable_resource_v<R> ||
|
||||
is_updatable_resource_v<R> ||
|
||||
is_creatable_resource_v<R> ||
|
||||
is_erasable_resource_v<R>
|
||||
> {
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
constexpr bool is_resource_v{is_resource<R>::value};
|
||||
|
||||
// has_location
|
||||
//
|
||||
|
||||
template<typename, typename = std::void_t<>>
|
||||
struct has_location : std::false_type {};
|
||||
|
||||
template<typename R>
|
||||
struct has_location<
|
||||
R,
|
||||
std::void_t<
|
||||
decltype(
|
||||
uri::uri_t{
|
||||
std::declval<R const>().location()
|
||||
}
|
||||
)
|
||||
>
|
||||
> : std::true_type{
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
constexpr bool has_location_v{has_location<R>::value};
|
||||
|
||||
// resource_traits
|
||||
//
|
||||
|
||||
template<typename R>
|
||||
struct resource_traits {
|
||||
using resource_type = R;
|
||||
|
||||
// properties
|
||||
//
|
||||
|
||||
static constexpr bool has_entity_tag{
|
||||
representation::traits::has_entity_tag_v<resource_type>
|
||||
};
|
||||
|
||||
static constexpr bool has_last_modified{
|
||||
representation::traits::has_last_modified_v<resource_type>
|
||||
};
|
||||
|
||||
static constexpr bool has_location{
|
||||
has_location_v<resource_type>
|
||||
};
|
||||
|
||||
static
|
||||
protocol::tokens_t
|
||||
allowed_methods()
|
||||
{
|
||||
protocol::tokens_t methods;
|
||||
|
||||
if constexpr (is_gettable) {
|
||||
methods.emplace_back("GET");
|
||||
methods.emplace_back("HEAD");
|
||||
}
|
||||
|
||||
if constexpr (is_updatable)
|
||||
methods.emplace_back("PUT");
|
||||
|
||||
if constexpr (is_creatable)
|
||||
methods.emplace_back("POST");
|
||||
|
||||
if constexpr (is_erasable)
|
||||
methods.emplace_back("DELETE");
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
// GET
|
||||
//
|
||||
static constexpr bool is_gettable{
|
||||
is_gettable_resource_v<resource_type>
|
||||
};
|
||||
|
||||
using get_traits = traits::get_traits<resource_type>;
|
||||
using get_representation_type = typename get_traits::result_type;
|
||||
using get_representation_type_traits = representation::traits::representation_traits<
|
||||
get_representation_type
|
||||
>;
|
||||
|
||||
// UPDATE
|
||||
//
|
||||
static constexpr bool is_updatable{
|
||||
is_updatable_resource_v<resource_type>
|
||||
};
|
||||
|
||||
using update_traits = traits::update_traits<resource_type>;
|
||||
using update_input_representation_type = typename update_traits::input_representation_type;
|
||||
using update_input_representation_type_traits = representation::traits::input_representation_traits<update_input_representation_type>;
|
||||
using update_result_type = typename update_traits::result_type;
|
||||
|
||||
// CREATE
|
||||
//
|
||||
static constexpr bool is_creatable{
|
||||
is_creatable_resource_v<resource_type>
|
||||
};
|
||||
|
||||
using create_traits = traits::create_traits<resource_type>;
|
||||
using create_input_representation_type = typename create_traits::input_representation_type;
|
||||
using create_input_representation_type_traits = representation::traits::input_representation_traits<create_input_representation_type>;
|
||||
using create_result_type = typename create_traits::result_type;
|
||||
|
||||
// ERASE
|
||||
//
|
||||
static constexpr bool is_erasable{
|
||||
is_erasable_resource_v<resource_type>
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace code::seafire::resources::traits
|
||||
|
||||
#endif
|
37
code/seafire/resources/version.hxx.in
Normal file
37
code/seafire/resources/version.hxx.in
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef code__seafire__resources__version_hxx_
|
||||
#define code__seafire__resources__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_RESOURCES_VERSION $libcode_seafire_resources.version.project_number$ULL
|
||||
#define LIBCODE_SEAFIRE_RESOURCES_VERSION_STR "$libcode_seafire_resources.version.project$"
|
||||
#define LIBCODE_SEAFIRE_RESOURCES_VERSION_ID "$libcode_seafire_resources.version.project_id$"
|
||||
#define LIBCODE_SEAFIRE_RESOURCES_VERSION_FULL "$libcode_seafire_resources.version$"
|
||||
|
||||
#define LIBCODE_SEAFIRE_RESOURCES_VERSION_MAJOR $libcode_seafire_resources.version.major$
|
||||
#define LIBCODE_SEAFIRE_RESOURCES_VERSION_MINOR $libcode_seafire_resources.version.minor$
|
||||
#define LIBCODE_SEAFIRE_RESOURCES_VERSION_PATCH $libcode_seafire_resources.version.patch$
|
||||
|
||||
#define LIBCODE_SEAFIRE_RESOURCES_PRE_RELEASE $libcode_seafire_resources.version.pre_release$
|
||||
|
||||
#define LIBCODE_SEAFIRE_RESOURCES_SNAPSHOT_SN $libcode_seafire_resources.version.snapshot_sn$ULL
|
||||
#define LIBCODE_SEAFIRE_RESOURCES_SNAPSHOT_ID "$libcode_seafire_resources.version.snapshot_id$"
|
||||
|
||||
#endif
|
16
manifest
Normal file
16
manifest
Normal file
@ -0,0 +1,16 @@
|
||||
: 1
|
||||
name: libcode-seafire-resources
|
||||
version: 0.1.0-a.0.z
|
||||
language: c++
|
||||
summary: libcode-seafire-resources 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-
|
||||
depends: libcode-seafire-representation ^0.1.0-
|
23
repositories.manifest
Normal file
23
repositories.manifest
Normal file
@ -0,0 +1,23 @@
|
||||
: 1
|
||||
summary: libcode-seafire-resources 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
|
||||
|
||||
:
|
||||
role: prerequisite
|
||||
location: https://code.helloryan.se/code/libcode-seafire-representation.git##HEAD
|
8
tests/.gitignore
vendored
Normal file
8
tests/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Test executables.
|
||||
#
|
||||
driver
|
||||
|
||||
# Testscript output directories (can be symlinks).
|
||||
#
|
||||
test
|
||||
test-*
|
4
tests/build/.gitignore
vendored
Normal file
4
tests/build/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/config.build
|
||||
/root/
|
||||
/bootstrap/
|
||||
build/
|
5
tests/build/bootstrap.build
Normal file
5
tests/build/bootstrap.build
Normal file
@ -0,0 +1,5 @@
|
||||
project = # Unnamed tests subproject.
|
||||
|
||||
using config
|
||||
using test
|
||||
using dist
|
16
tests/build/root.build
Normal file
16
tests/build/root.build
Normal file
@ -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
|
1
tests/buildfile
Normal file
1
tests/buildfile
Normal file
@ -0,0 +1 @@
|
||||
./: {*/ -build/}
|
Loading…
x
Reference in New Issue
Block a user