Hello Seafire

This commit is contained in:
R.Y.A.N 2025-03-07 02:25:53 +01:00
commit a7eb1cfba7
Signed by: R.Y.A.N
GPG Key ID: 3BD93EABD1407B82
34 changed files with 1192 additions and 0 deletions

17
.editorconfig Normal file
View 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
View File

@ -0,0 +1 @@
* text=auto

32
.gitignore vendored Normal file
View File

@ -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

32
LICENSE.md Normal file
View File

@ -0,0 +1,32 @@
Copyright © 2025 Per Ryan Edin. 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 Seafire, an HTTP/1.1 implementation
> for C++ applications. 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.

10
README.md Normal file
View File

@ -0,0 +1,10 @@
# Seafire
## 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 repository
write access.

4
build/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/

7
build/bootstrap.build Normal file
View File

@ -0,0 +1,7 @@
project = seafire-representation
using version
using config
using test
using install
using dist

6
build/export.build Normal file
View File

@ -0,0 +1,6 @@
$out_root/
{
include seafire/representation/
}
export $out_root/seafire/representation/$import.target

16
build/root.build Normal file
View 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
View File

@ -0,0 +1,5 @@
./: {seafire/ tests/} doc{README.md} legal{LICENSE.md} manifest
# Don't install tests.
#
tests/: install = false

15
manifest Normal file
View File

@ -0,0 +1,15 @@
: 1
name: seafire-representation
version: 0.1.0-a.0.z
language: c++
summary: Seafire Representation C++ library
license: BSD-4-Clause
description-file: README.md
url: https://helloryan.se/
email: ryan@helloryan.se
depends: * build2 >= 0.17.0
depends: * bpkg >= 0.17.0
depends: libasio ^1.29.0
depends: seafire-common ^0.1.0-
depends: seafire-protocol ^0.1.0-
depends: seafire-server ^0.1.0-

19
repositories.manifest Normal file
View File

@ -0,0 +1,19 @@
: 1
summary: 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/creatures/Seafire-Common.git##HEAD
:
role: prerequisite
location: https://code.helloryan.se/creatures/Seafire-Protocol.git##HEAD
:
role: prerequisite
location: https://code.helloryan.se/creatures/Seafire-Server.git##HEAD

9
seafire/representation/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
# Generated version header.
#
version.hxx
# Unit test executables and Testscript output directories
# (can be symlinks).
#
*.test
test-*.test

View File

@ -0,0 +1,67 @@
intf_libs = # Interface dependencies.
impl_libs = # Implementation dependencies.
import intf_libs =+ libasio%lib{asio}
import intf_libs =+ seafire-common%lib{seafire-common}
import intf_libs =+ seafire-protocol%lib{seafire-protocol}
import intf_libs =+ seafire-server%lib{seafire-server}
./: lib{seafire-representation}: libul{seafire-representation}
libul{seafire-representation}: {hxx ixx txx cxx}{** -**.test... -version} \
{hxx }{ version}
libul{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{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{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{seafire-representation}: bin.lib.version = "-$version.project_id"
else
lib{seafire-representation}: bin.lib.version = "-$version.major.$version.minor"
# Install into the seafire/representation/ subdirectory of, say, /usr/include/
# recreating subdirectories.
#
{hxx ixx txx}{*}:
{
install = include/seafire/representation/
install.subdirs = true
}

View File

@ -0,0 +1,20 @@
#ifndef seafire__representation__concepts_hxx_
#define seafire__representation__concepts_hxx_
#include <seafire/representation/traits.hxx>
namespace seafire::representation
{
template<typename R>
concept BasicRepresentation = traits::is_basic_representation_v<R>;
template<typename R>
concept ContentNegotiableRepresentation = traits::is_content_negotiable_representation_v<R>;
template<typename R>
concept Representation = traits::is_representation_v<R>;
} // namespace seafire::representation
#endif

View File

@ -0,0 +1,56 @@
#ifndef seafire__representation__content_negotiable_hxx_
#define seafire__representation__content_negotiable_hxx_
#include <seafire/representation/representation.hxx>
#include <optional>
namespace seafire::representation
{
template<typename T, typename... Formats>
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<protocol::media_type_t> const&) const;
static
bool
is_accepted(std::optional<protocol::media_type_t> const&);
private:
template<
typename CurrentFormat,
typename... MoreFormats
>
representation_t
select(std::optional<protocol::media_type_t> const&) const;
template<
typename CurrentFormat,
typename... MoreFormats
>
static
bool
is_accepted(std::optional<protocol::media_type_t> const&);
};
} // namespace seafire::representation
#include <seafire/representation/content-negotiable.txx>
#endif

View File

@ -0,0 +1,90 @@
namespace seafire::representation
{
template<
typename T,
typename... Formats
>
representation_t
content_negotiable_t<T, Formats...>::
select(std::optional<protocol::media_type_t> const& type) const
{
return select<Formats...>(type);
}
template<
typename T,
typename... Formats
>
bool
content_negotiable_t<T, Formats...>::
is_accepted(std::optional<protocol::media_type_t> const& type)
{
return is_accepted<Formats...>(type);
}
template<
typename T,
typename... Formats
>
template<
typename CurrentFormat,
typename... MoreFormats
>
representation_t
content_negotiable_t<T, Formats...>::
select(std::optional<protocol::media_type_t> 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<MoreFormats...>(type);
}
// fixme: not acceptable, throw something.
//
throw "temporary"; // fixme: change
}
template<
typename T,
typename... Formats
>
template<
typename CurrentFormat,
typename... MoreFormats
>
bool
content_negotiable_t<T, Formats...>::
is_accepted(std::optional<protocol::media_type_t> 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<MoreFormats...>(type);
}
return false;
}
} // namespace seafire::representation

View File

@ -0,0 +1,64 @@
#ifndef seafire__representation__metadata_hxx_
#define seafire__representation__metadata_hxx_
#include <seafire/protocol/media-type.hxx>
#include <seafire/protocol/rfc7232/entity-tag.hxx>
#include <seafire/representation/concepts.hxx>
#include <seafire/representation/representation.hxx>
#include <seafire/representation/traits.hxx>
#include <code/uri/uri.hxx>
#include <chrono>
#include <optional>
namespace seafire::representation
{
// etag
//
template<BasicRepresentation BR>
std::optional<protocol::rfc7232::entity_tag_t>
get_etag(BR const& rep)
{
using local_traits = 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_t const& rep)
{
return rep.etag();
}
// last-modified
//
template<BasicRepresentation BR>
std::optional<std::chrono::system_clock::time_point>
get_last_modified(BR const& rep)
{
using local_traits = 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_t const& rep)
{
return rep.last_modified();
}
} // namespace seafire::representation
#endif

View File

@ -0,0 +1,29 @@
#ifndef seafire__representation__negotiate_hxx_
#define seafire__representation__negotiate_hxx_
#include <seafire/protocol/media-type.hxx>
#include <seafire/protocol/rfc7231/accept.hxx>
#include <seafire/representation/concepts.hxx>
#include <seafire/representation/traits.hxx>
#include <seafire/server/request.hxx>
#include <seafire/server/response.hxx>
#include <optional>
namespace seafire::representation
{
// fixme: implement concept for handler.
//
template<Representation R, typename Handler>
void
negotiate(server::request_t& req,
server::response_t& res,
Handler&& handler);
} // namespace seafire::representation
#include <seafire/representation/negotiate.txx>
#endif

View File

@ -0,0 +1,33 @@
namespace seafire::representation
{
template<representation::Representation R, typename Handler>
void
negotiate(server::request_t& req,
server::response_t& res,
Handler&& handler)
{
using t = traits::representation_traits<R>;
if constexpr (t::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 seafire::representation

View File

@ -0,0 +1,38 @@
#include <seafire/representation/representation.hxx>
namespace seafire::representation
{
protocol::media_type_t
representation_t::
type() const
{
return r_->type();
}
std::optional<protocol::rfc7232::entity_tag_t>
representation_t::
etag() const
{
return r_->etag();
}
std::optional<std::chrono::system_clock::time_point>
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 seafire::representation

View File

@ -0,0 +1,101 @@
#ifndef seafire__representation__representation_hxx_
#define seafire__representation__representation_hxx_
#include <seafire/protocol/media-type.hxx>
#include <seafire/protocol/rfc7231/content-type.hxx>
#include <seafire/protocol/rfc7232/entity-tag.hxx>
#include <seafire/protocol/rfc7232/etag.hxx>
#include <seafire/protocol/rfc7232/last-modified.hxx>
#include <seafire/representation/concepts.hxx>
#include <seafire/representation/traits.hxx>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <vector>
namespace seafire::representation
{
class representation_t
{
public:
template<BasicRepresentation R>
representation_t(R r);
protocol::media_type_t
type() const;
std::optional<protocol::rfc7232::entity_tag_t>
etag() const;
std::optional<std::chrono::system_clock::time_point>
last_modified() const;
protocol::media_type_t
write_to(std::ostream& o) const;
private:
struct concept_t;
template<typename R>
struct container_t;
std::shared_ptr<concept_t const> r_;
};
struct representation_t::concept_t
{
virtual
~concept_t();
virtual
protocol::media_type_t
type() const = 0;
virtual
std::optional<protocol::rfc7232::entity_tag_t>
etag() const = 0;
virtual
std::optional<std::chrono::system_clock::time_point>
last_modified() const = 0;
virtual
void
write_to(std::ostream& o) const = 0;
};
template<BasicRepresentation R>
struct representation_t::container_t<R>
: representation_t::concept_t
{
using representation_type = R;
using representation_traits = traits::representation_traits<representation_type>;
container_t(representation_type r);
protocol::media_type_t
type() const override;
std::optional<protocol::rfc7232::entity_tag_t>
etag() const override;
std::optional<std::chrono::system_clock::time_point>
last_modified() const override;
void
write_to(std::ostream& o) const override;
representation_type r;
};
} // namespace seafire::representation
#include <seafire/representation/representation.txx>
#endif

View File

@ -0,0 +1,54 @@
namespace seafire::representation
{
template<BasicRepresentation R>
representation_t::
representation_t(R r)
: r_{std::make_shared<container_t<R>>(std::move(r))}
{}
template<BasicRepresentation R>
representation_t::container_t<R>::
container_t(representation_type r)
: r{std::move(r)}
{}
template<BasicRepresentation R>
protocol::media_type_t
representation_t::container_t<R>::
type() const
{
return r.type();
}
template<BasicRepresentation R>
std::optional<protocol::rfc7232::entity_tag_t>
representation_t::container_t<R>::
etag() const
{
if constexpr (representation_traits::has_entity_tag)
return r.etag();
return {};
}
template<BasicRepresentation R>
std::optional<std::chrono::system_clock::time_point>
representation_t::container_t<R>::
last_modified() const
{
if constexpr (representation_traits::has_last_modified)
return r.last_modified();
return {};
}
template<BasicRepresentation R>
void
representation_t::container_t<R>::
write_to(std::ostream& o) const
{
r.write_to(o);
}
} // namespace seafire::representation

View File

@ -0,0 +1,23 @@
#ifndef seafire__representation__select_hxx_
#define seafire__representation__select_hxx_
#include <seafire/protocol/media-type.hxx>
#include <seafire/representation/concepts.hxx>
#include <seafire/representation/representation.hxx>
#include <seafire/representation/traits.hxx>
#include <optional>
namespace seafire::representation
{
template<Representation R>
representation_t
select(R const&, std::optional<protocol::media_type_t> const&);
} // namespace seafire::representation
#include <seafire/representation/select.txx>
#endif

View File

@ -0,0 +1,22 @@
namespace seafire::representation
{
template<Representation R>
representation_t
select(R const& rep, std::optional<protocol::media_type_t> const& accepted_type)
{
using representation_traits = traits::representation_traits<R>;
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 seafire::representation

View File

@ -0,0 +1,27 @@
#ifndef seafire__representation__send_hxx_
#define seafire__representation__send_hxx_
#include <seafire/representation/concepts.hxx>
#include <seafire/representation/metadata.hxx>
#include <seafire/protocol/rfc7231/content-type.hxx>
#include <seafire/server/request.hxx>
#include <seafire/server/response.hxx>
namespace seafire::representation
{
template<Representation R>
void
send(server::request_t&,
server::response_t&,
protocol::status_code_t,
R const&,
bool);
} // namespace seafire::representation
#include <seafire/representation/send.txx>
#endif

View File

@ -0,0 +1,36 @@
namespace seafire::representation
{
template<Representation R>
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<rfc7231::content_type_t>(res, rep.type());
if (auto etag = get_etag(rep); etag) {
set<rfc7232::etag_t>(res, *etag);
}
if (auto last_modified = get_last_modified(rep); last_modified) {
set<rfc7232::last_modified_t>(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 seafire::representation

View File

@ -0,0 +1,288 @@
#ifndef seafire__representation__traits_hxx_
#define seafire__representation__traits_hxx_
#include <seafire/common/traits.hxx>
#include <seafire/protocol/traits.hxx>
#include <seafire/protocol/media-type.hxx>
#include <seafire/protocol/rfc7232/entity-tag.hxx>
#include <seafire/protocol/rfc7232/last-modified.hxx>
#include <optional>
#include <tuple>
#include <type_traits>
#include <variant>
namespace seafire::representation::traits
{
// has_entity_tag
//
template<typename, typename = std::void_t<>>
struct has_entity_tag
: std::false_type
{};
template<typename R>
struct has_entity_tag<
R,
std::void_t<
decltype(
protocol::rfc7232::entity_tag_t{
std::declval<
common::traits::remove_optional_t<decltype(std::declval<R const>().etag())>
>()
}
)
>
> : std::true_type
{};
template<typename R>
constexpr bool has_entity_tag_v{has_entity_tag<R>::value};
// has_last_modified
//
template<typename, typename = std::void_t<>>
struct has_last_modified
: std::false_type
{};
template<typename R>
struct has_last_modified<
R,
std::void_t<
decltype(
std::chrono::system_clock::time_point{
std::declval<
common::traits::remove_optional_t<decltype(std::declval<R const>().last_modified())>
>()
}
)
>
> : std::true_type
{};
template<typename R>
constexpr bool has_last_modified_v{has_last_modified<R>::value};
// =============================================================================
// input representation.
//
template<
typename R,
typename = std::void_t<>
>
struct is_input_representation
: std::false_type
{};
template<typename M>
struct is_input_representation<
M,
std::void_t<
decltype(
bool{
M::can_accept_input(
std::declval<protocol::media_type_t>()
)
}
),
decltype(
M{
std::declval<
decltype(
M::read_from(
std::declval<std::optional<protocol::media_type_t>>(),
std::declval<std::istream&>()
)
)
>()
}
)
>
> : std::true_type {
};
template<typename... M>
struct is_input_representation<
std::variant<M...>
> : std::bool_constant<
(is_input_representation<M>::value && ...)
> {
};
template<typename M>
constexpr bool is_input_representation_v{is_input_representation<M>::value};
template<typename M>
struct is_variant_input_representation : std::false_type {};
template<typename... M>
struct is_variant_input_representation<
std::variant<M...>
> : is_input_representation<std::variant<M...>> {
};
template<typename M>
constexpr bool is_variant_input_representation_v{is_variant_input_representation<M>::value};
template<typename M>
struct input_representation_traits
{
// TODO: DO.
};
// =============================================================================
// (output) representation.
//
// is_basic_representation<R>
//
template<
typename R,
typename = std::void_t<>
>
struct is_basic_representation
: std::false_type
{};
template<typename R>
struct is_basic_representation<
R,
std::void_t<
decltype(
protocol::media_type_t{
std::declval<R const>().type()
}
),
decltype(
std::declval<R const>().write_to(std::declval<std::ostream&>())
)
>
> : std::true_type
{};
template<typename R>
constexpr bool is_basic_representation_v{
is_basic_representation<R>::value
};
// is_content_negotiable_representation<R>
//
template<typename R, typename = std::void_t<>>
struct is_content_negotiable_representation
: std::false_type
{};
template<typename R>
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<std::optional<protocol::media_type_t>>()
)
}
),
// check that r{}.select(protocol::media_type_t) is valid.
//
decltype(
std::declval<R const>().select(
std::declval<std::optional<protocol::media_type_t>>()
)
)
>
> : std::bool_constant<
is_basic_representation_v<
// check that the type of the return value of r{}.select(...) is a representation.
//
decltype(
std::declval<R const>().select(
std::declval<std::optional<protocol::media_type_t>>()
)
)
>
>
{};
template<typename R>
constexpr bool is_content_negotiable_representation_v{
is_content_negotiable_representation<R>::value
};
// is_representation<R>
//
template<typename R>
struct is_representation : std::bool_constant<
is_basic_representation_v<R>
|| is_content_negotiable_representation_v<R>
>
{};
template<typename R>
constexpr bool is_representation_v{is_representation<R>::value};
template<typename R>
struct is_optional_representation
: std::false_type
{};
template<typename R>
struct is_optional_representation<std::optional<R>> : std::bool_constant<
is_basic_representation_v<R>
|| is_content_negotiable_representation_v<R>
>
{};
// is_optional_representation<R>
//
template<typename R>
constexpr bool is_optional_representation_v{is_optional_representation<R>::value};
// representation_traits<R>
//
template<typename R>
struct representation_traits
{
using representation_type = common::traits::remove_optional_t<R>;
static constexpr bool is_optional{
// Using R here since optional<> is removed from representation_type.
is_optional_representation_v<R>
};
static constexpr bool is_basic {
is_basic_representation_v<representation_type>
};
static constexpr bool is_content_negotiable {
is_content_negotiable_representation_v<representation_type>
};
static constexpr bool has_entity_tag{
has_entity_tag_v<representation_type>
};
static constexpr bool has_last_modified{
has_last_modified_v<representation_type>
};
};
} // namespace seafire::representation::traits
#endif

View File

@ -0,0 +1,37 @@
#ifndef seafire__representation__version_hxx_
#define 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 SEAFIRE_REPRESENTATION_VERSION $seafire_representation.version.project_number$ULL
#define SEAFIRE_REPRESENTATION_VERSION_STR "$seafire_representation.version.project$"
#define SEAFIRE_REPRESENTATION_VERSION_ID "$seafire_representation.version.project_id$"
#define SEAFIRE_REPRESENTATION_VERSION_FULL "$seafire_representation.version$"
#define SEAFIRE_REPRESENTATION_VERSION_MAJOR $seafire_representation.version.major$
#define SEAFIRE_REPRESENTATION_VERSION_MINOR $seafire_representation.version.minor$
#define SEAFIRE_REPRESENTATION_VERSION_PATCH $seafire_representation.version.patch$
#define SEAFIRE_REPRESENTATION_PRE_RELEASE $seafire_representation.version.pre_release$
#define SEAFIRE_REPRESENTATION_SNAPSHOT_SN $seafire_representation.version.snapshot_sn$ULL
#define SEAFIRE_REPRESENTATION_SNAPSHOT_ID "$seafire_representation.version.snapshot_id$"
#endif

8
tests/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Test executables.
#
driver
# Testscript output directories (can be symlinks).
#
test
test-*

4
tests/build/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/

View File

@ -0,0 +1,5 @@
project = # Unnamed tests subproject.
using config
using test
using dist

16
tests/build/root.build Normal file
View 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
View File

@ -0,0 +1 @@
./: {*/ -build/}