1#ifndef SOURCEMETA_CORE_URI_H_
2#define SOURCEMETA_CORE_URI_H_
4#ifndef SOURCEMETA_CORE_URI_EXPORT
5#include <sourcemeta/core/uri_export.h>
8#include <sourcemeta/core/text.h>
11#include <sourcemeta/core/uri_error.h>
39namespace sourcemeta::core {
43class SOURCEMETA_CORE_URI_EXPORT
URI {
55 auto operator=(const
URI &) ->
URI & = default;
58 auto operator=(
URI &&) noexcept ->
URI & = default;
68 requires std::convertible_to<T, std::string_view> &&
69 (!std::is_same_v<std::decay_t<T>,
URI>)
70 explicit
URI(T &&input) {
71 this->parse(std::string_view{std::forward<T>(input)});
83 URI(std::istream &input);
105 [[nodiscard]] auto
is_urn() const ->
bool;
116 [[nodiscard]] auto
is_tag() const ->
bool;
251 [[nodiscard]] auto
empty() const ->
bool;
263 [[nodiscard]] auto
scheme() const -> std::optional<std::string_view>;
275 [[nodiscard]] auto
host() const -> std::optional<std::string_view>;
289 [[nodiscard]] auto
port() const -> std::optional<std::uint32_t>;
309 [[nodiscard]] auto
authority() const -> std::optional<std::
string>;
322 [[nodiscard]] auto
path() const -> std::optional<std::string_view>;
422 [[nodiscard]] auto
fragment() const -> std::optional<std::string_view>;
453 class SOURCEMETA_CORE_URI_EXPORT
Query {
479 [[nodiscard]]
auto raw() const -> std::string_view;
497 [[nodiscard]] auto
at(const std::string_view name) const
498 -> std::optional<std::string_view>;
503 class SOURCEMETA_CORE_URI_EXPORT const_iterator {
505 using iterator_category = std::forward_iterator_tag;
506 using value_type = std::pair<std::string_view, std::string_view>;
507 using difference_type = std::ptrdiff_t;
508 using reference =
const value_type &;
509 using pointer =
const value_type *;
511 const_iterator() =
default;
512 const_iterator(
const std::string_view
raw,
const std::size_t pair_start);
514 [[nodiscard]]
auto operator*()
const -> reference;
515 [[nodiscard]]
auto operator->()
const -> pointer;
516 auto operator++() -> const_iterator &;
517 auto operator++(
int) -> const_iterator;
518 [[nodiscard]]
auto operator==(
const const_iterator &other)
const -> bool;
519 [[nodiscard]]
auto operator!=(
const const_iterator &other)
const -> bool;
523#pragma warning(disable : 4251)
525 std::string_view raw_{};
526 std::size_t pair_start_{std::string_view::npos};
527 value_type current_{};
529#pragma warning(default : 4251)
534 [[nodiscard]]
auto begin() const -> const_iterator;
537 [[nodiscard]] auto end() const -> const_iterator;
541#pragma warning(disable : 4251)
543 std::string_view raw_;
545#pragma warning(default : 4251)
619 -> std::optional<std::
string>;
751 [[nodiscard]] auto
userinfo() const -> std::optional<std::string_view>;
769 auto operator==(const
URI &other) const noexcept ->
bool;
772 auto operator<(const
URI &other) const noexcept ->
bool;
853 [[nodiscard]] static auto
escape(std::string_view input,
854 bool maybe_encoded = false) -> std::
string;
871 template <typename Output>
872 static auto
escape(const std::string_view input, Output &output,
873 const
bool maybe_encoded = false) ->
void {
874 output.reserve(output.size() + input.size() * 3);
875 for (std::size_t position = 0; position < input.size();) {
876 auto byte{
static_cast<unsigned char>(input[position])};
877 std::size_t advance{1};
881 if (maybe_encoded && input[position] ==
'%' &&
882 position + 2 < input.size()) {
884 const auto low{high < 0 ? static_cast<std::int8_t>(-1)
887 byte =
static_cast<unsigned char>((high << 4) | low);
894 if (
is_alphanum(
static_cast<char>(
byte)) ||
byte ==
'-' ||
byte ==
'.' ||
895 byte ==
'_' ||
byte ==
'~') {
896 output.push_back(
static_cast<char>(
byte));
899 const auto high{
static_cast<unsigned char>((
byte >> 4U) & 0x0FU)};
900 const auto low{
static_cast<unsigned char>(
byte & 0x0FU)};
901 output.push_back(
'%');
903 static_cast<char>(high < 10 ?
'0' + high :
'A' + high - 10));
905 static_cast<char>(low < 10 ?
'0' + low :
'A' + low - 10));
930 template <
typename Output>
932 const std::string_view value,
933 const char opener =
'?') ->
void {
934 if (!sink.empty() && sink.back() != opener && sink.back() !=
'&') {
953 [[nodiscard]]
static auto unescape(std::string_view input) -> std::string;
973 template <
typename Output>
975 Output &output) ->
bool {
976 const auto base{output.size()};
977 output.reserve(base + input.size());
978 for (std::size_t position = 0; position < input.size();) {
979 const auto character{input[position]};
980 if (character ==
'+') {
981 output.push_back(
' ');
983 }
else if (character ==
'%') {
984 const auto high{position + 2 < input.size()
986 :
static_cast<std::int8_t
>(-1)};
987 const auto low{high < 0 ? static_cast<std::int8_t>(-1)
990 output.resize(base,
'\0');
994 output.push_back(
static_cast<char>((high << 4) | low));
997 output.push_back(character);
1029 [[nodiscard]]
static auto is_scheme(std::string_view input)
noexcept -> bool;
1054 [[nodiscard]]
static auto is_uri(std::string_view input)
noexcept -> bool;
1081 [[nodiscard]]
static auto is_iri(std::string_view input)
noexcept -> bool;
1110 std::string_view prefix)
1111 -> std::optional<std::string>;
1125 std::string_view old_prefix,
1126 std::string_view new_prefix)
1127 -> std::optional<std::string>;
1130 auto parse(std::string_view input) -> void;
1135#if defined(_MSC_VER)
1136#pragma warning(disable : 4251)
1138 std::optional<std::string> path_{};
1139 std::optional<std::string> userinfo_{};
1140 std::optional<std::string> host_{};
1141 std::optional<std::uint32_t> port_{};
1142 std::optional<std::string> scheme_{};
1143 std::optional<std::string> fragment_{};
1144 std::optional<std::string> query_{};
1145 bool ip_literal_{
false};
1149#if defined(_MSC_VER)
1150#pragma warning(default : 4251)
constexpr auto is_alphanum(const Character character) noexcept -> bool
Definition text.h:289
auto hex_digit_value(const char character) noexcept -> std::int8_t
Definition text.h:752
static auto unescape_form(const std::string_view input, Output &output) -> bool
Definition uri.h:974
static auto is_scheme(std::string_view input) noexcept -> bool
auto is_http() const -> bool
auto is_ipv4() const -> bool
auto append_path(std::string_view path) -> URI &
auto relative_to(const URI &base) -> URI &
auto to_path() const -> std::filesystem::path
auto is_urn() const -> bool
auto is_relative() const -> bool
auto scheme() const -> std::optional< std::string_view >
static auto from_path(const std::filesystem::path &path) -> URI
auto recompose_without_fragment() const -> std::optional< std::string >
auto is_mailto() const -> bool
auto is_ipv6() const -> bool
auto path() const -> std::optional< std::string_view >
auto is_https() const -> bool
URI(URI &&) noexcept=default
Move constructor.
auto resolve_from(const URI &base) -> URI &
auto recompose() const -> std::string
static auto is_iri(std::string_view input) noexcept -> bool
static auto is_iri_reference(std::string_view input) noexcept -> bool
URI(const URI &)=default
Copy constructor.
URI()=default
Default constructor creates an empty URI.
auto is_localhost() const -> bool
auto fragment() const -> std::optional< std::string_view >
static auto unescape(std::string_view input) -> std::string
auto has_same_authority(const URI &other) const noexcept -> bool
auto is_fragment_only() const -> bool
auto canonicalize() -> URI &
auto is_internationalized() const noexcept -> bool
static auto normalize_path(std::string_view path) -> std::string
static auto append_query_parameter(Output &sink, const std::string_view name, const std::string_view value, const char opener='?') -> void
Definition uri.h:931
auto host() const -> std::optional< std::string_view >
auto is_absolute() const noexcept -> bool
auto userinfo() const -> std::optional< std::string_view >
auto is_file() const -> bool
auto empty() const -> bool
static auto is_uri_reference(std::string_view input) noexcept -> bool
auto is_loopback() const -> bool
auto is_tag() const -> bool
auto rebase(const URI &base, const URI &new_base) -> URI &
static auto escape(std::string_view input, bool maybe_encoded=false) -> std::string
auto authority() const -> std::optional< std::string >
static auto is_gen_delim(char character) noexcept -> bool
auto recompose_relative() const -> std::string
auto port() const -> std::optional< std::uint32_t >
static auto from_iri(std::string_view input) -> URI
static auto strip_path_prefix(std::string_view path, std::string_view prefix) -> std::optional< std::string >
auto extension(std::string &&extension) -> URI &
static auto rebase_path(std::string_view path, std::string_view old_prefix, std::string_view new_prefix) -> std::optional< std::string >
auto query() const -> std::optional< Query >
static auto from_fragment(const std::string_view fragment) -> URI
static auto is_uri(std::string_view input) noexcept -> bool