Sourcemeta Core 0.0.0
Loading...
Searching...
No Matches
uri.h
1#ifndef SOURCEMETA_CORE_URI_H_
2#define SOURCEMETA_CORE_URI_H_
3
4#ifndef SOURCEMETA_CORE_URI_EXPORT
5#include <sourcemeta/core/uri_export.h>
6#endif
7
8#include <sourcemeta/core/text.h>
9
10// NOLINTBEGIN(misc-include-cleaner)
11#include <sourcemeta/core/uri_error.h>
12// NOLINTEND(misc-include-cleaner)
13
14#include <array> // std::array
15#include <concepts> // std::convertible_to, std::same_as
16#include <cstddef> // std::size_t, std::ptrdiff_t
17#include <cstdint> // std::uint8_t, std::uint32_t
18#include <filesystem> // std::filesystem
19#include <istream> // std::istream
20#include <iterator> // std::forward_iterator_tag
21#include <memory> // std::unique_ptr
22#include <optional> // std::optional
23#include <span> // std::span
24#include <string> // std::string
25#include <string_view> // std::string_view
26#include <type_traits> // std::is_same_v, std::remove_cvref_t
27#include <utility> // std::pair, std::forward
28#include <vector> // std::vector
29
39
40namespace sourcemeta::core {
41
44class SOURCEMETA_CORE_URI_EXPORT URI {
45public:
47 URI() = default;
48
50 URI(const URI &) = default;
51
53 URI(URI &&) noexcept = default;
54
56 auto operator=(const URI &) -> URI & = default;
57
59 auto operator=(URI &&) noexcept -> URI & = default;
60
68 template <typename T>
69 requires std::convertible_to<T, std::string_view> &&
70 (!std::is_same_v<std::decay_t<T>, URI>)
71 explicit URI(T &&input) {
72 this->parse(std::string_view{std::forward<T>(input)});
73 }
74
84 URI(std::istream &input);
85
95 [[nodiscard]] auto is_absolute() const noexcept -> bool;
96
106 [[nodiscard]] auto is_urn() const -> bool;
107
117 [[nodiscard]] auto is_tag() const -> bool;
118
128 [[nodiscard]] auto is_mailto() const -> bool;
129
139 [[nodiscard]] auto is_file() const -> bool;
140
151 [[nodiscard]] auto is_http() const -> bool;
152
163 [[nodiscard]] auto is_https() const -> bool;
164
174 [[nodiscard]] auto is_fragment_only() const -> bool;
175
185 [[nodiscard]] auto is_relative() const -> bool;
186
196 [[nodiscard]] auto is_ipv4() const -> bool;
197
207 [[nodiscard]] auto is_ipv6() const -> bool;
208
225 [[nodiscard]] auto is_loopback() const -> bool;
226
241 [[nodiscard]] auto is_localhost() const -> bool;
242
252 [[nodiscard]] auto empty() const -> bool;
253
264 [[nodiscard]] auto scheme() const -> std::optional<std::string_view>;
265
276 [[nodiscard]] auto host() const -> std::optional<std::string_view>;
277
290 [[nodiscard]] auto port() const -> std::optional<std::uint32_t>;
291
310 [[nodiscard]] auto authority() const -> std::optional<std::string>;
311
323 [[nodiscard]] auto path() const -> std::optional<std::string_view>;
324
337 auto path(const std::string &path) -> URI &;
338
351 auto path(std::string &&path) -> URI &;
352
366 auto append_path(std::string_view path) -> URI &;
367
381 auto append_path(const URI &reference) -> URI &;
382
398 auto append_path(URI &&reference) -> URI &;
399
411 auto extension(std::string &&extension) -> URI &;
412
423 [[nodiscard]] auto fragment() const -> std::optional<std::string_view>;
424
438 auto fragment(const std::string_view fragment) -> URI &;
439
451 auto unescaped_fragment(const std::string_view fragment) -> URI &;
452
468 class SOURCEMETA_CORE_URI_EXPORT Query {
469 public:
481 explicit Query(const std::string_view raw);
482
494 [[nodiscard]] auto raw() const -> std::string_view;
495
512 [[nodiscard]] auto at(const std::string_view name) const
513 -> std::optional<std::string_view>;
514
518 class SOURCEMETA_CORE_URI_EXPORT const_iterator {
519 public:
520 using iterator_category = std::forward_iterator_tag;
521 using value_type = std::pair<std::string_view, std::string_view>;
522 using difference_type = std::ptrdiff_t;
523 using reference = const value_type &;
524 using pointer = const value_type *;
525
526 const_iterator() = default;
527 const_iterator(const std::string_view raw, const std::size_t pair_start);
528
529 [[nodiscard]] auto operator*() const -> reference;
530 [[nodiscard]] auto operator->() const -> pointer;
531 auto operator++() -> const_iterator &;
532 auto operator++(int) -> const_iterator;
533 [[nodiscard]] auto operator==(const const_iterator &other) const -> bool;
534 [[nodiscard]] auto operator!=(const const_iterator &other) const -> bool;
535
536 private:
537#if defined(_MSC_VER)
538#pragma warning(push)
539#pragma warning(disable : 4251)
540#endif
541 std::string_view raw_{};
542 std::size_t pair_start_{std::string_view::npos};
543 value_type current_{};
544#if defined(_MSC_VER)
545#pragma warning(pop)
546#endif
547 };
548
550 [[nodiscard]] auto begin() const -> const_iterator;
551
553 [[nodiscard]] auto end() const -> const_iterator;
554
555 private:
556#if defined(_MSC_VER)
557#pragma warning(push)
558#pragma warning(disable : 4251)
559#endif
560 std::string_view raw_;
561#if defined(_MSC_VER)
562#pragma warning(pop)
563#endif
564 };
565
578 [[nodiscard]] auto query() const -> std::optional<Query>;
579
592 auto query(const std::string_view query) -> URI &;
593
604 [[nodiscard]] auto recompose() const -> std::string;
605
619 [[nodiscard]] auto recompose_relative() const -> std::string;
620
635 [[nodiscard]] auto recompose_without_fragment() const
636 -> std::optional<std::string>;
637
648 auto canonicalize() -> URI &;
649
660 [[nodiscard]] auto to_path() const -> std::filesystem::path;
661
674 auto resolve_from(const URI &base) -> URI &;
675
701 auto relative_to(const URI &base) -> URI &;
702
718 auto rebase(const URI &base, const URI &new_base) -> URI &;
719
735 auto rebase(const URI &base, URI &&new_base) -> URI &;
736
751 [[nodiscard]] auto has_same_authority(const URI &other) const noexcept
752 -> bool;
753
768 [[nodiscard]] auto userinfo() const -> std::optional<std::string_view>;
769
782 auto userinfo(const std::string_view userinfo) -> URI &;
783
786 auto operator==(const URI &other) const noexcept -> bool;
787
789 auto operator<(const URI &other) const noexcept -> bool;
790
802 static auto from_fragment(const std::string_view fragment) -> URI;
803
815 static auto from_unescaped_fragment(const std::string_view fragment) -> URI;
816
828 static auto from_path(const std::filesystem::path &path) -> URI;
829
842 static auto from_iri(std::string_view input) -> URI;
843
856 [[nodiscard]] auto is_internationalized() const noexcept -> bool;
857
869 static auto canonicalize(std::string_view input) -> std::string;
870
884 [[nodiscard]] static auto escape(std::string_view input,
885 bool maybe_encoded = false) -> std::string;
886
902 template <typename Output>
903 static auto escape(const std::string_view input, Output &output,
904 const bool maybe_encoded = false) -> void {
905 output.reserve(output.size() + (input.size() * 3));
906 for (std::size_t position = 0; position < input.size();) {
907 auto byte{static_cast<unsigned char>(input[position])};
908 std::size_t advance{1};
909 // Treat the input as possibly already encoded by decoding each valid
910 // escape before re-encoding, so a valid escape survives and a needlessly
911 // encoded unreserved octet is decoded
912 if (maybe_encoded && input[position] == '%' &&
913 position + 2 < input.size()) {
914 const auto high{hex_digit_value(input[position + 1])};
915 const auto low{high < 0 ? static_cast<std::int8_t>(-1)
916 : hex_digit_value(input[position + 2])};
917 if (low >= 0) {
918 byte = static_cast<unsigned char>((high << 4) | low);
919 advance = 3;
920 }
921 }
922
923 position += advance;
924 // RFC 3986 Section 2.3: the unreserved set passes through unescaped
925 if (is_alphanum(static_cast<char>(byte)) || byte == '-' || byte == '.' ||
926 byte == '_' || byte == '~') {
927 output.push_back(static_cast<char>(byte));
928 } else {
929 // RFC 3986 Section 2.1: percent-encode with uppercase hexadecimal
930 const auto high{static_cast<unsigned char>((byte >> 4U) & 0x0FU)};
931 const auto low{static_cast<unsigned char>(byte & 0x0FU)};
932 output.push_back('%');
933 output.push_back(
934 static_cast<char>(high < 10 ? '0' + high : 'A' + high - 10));
935 output.push_back(
936 static_cast<char>(low < 10 ? '0' + low : 'A' + low - 10));
937 }
938 }
939 }
940
962 template <typename Output>
963 static auto escape_reference(const std::string_view input, Output &output)
964 -> void {
965 // RFC 3986 Section 2.2 and Section 2.3: the unreserved characters, the
966 // sub-delims, and the gen-delims other than the square brackets and the
967 // number sign
968 static constexpr std::array<std::uint8_t, 256> PASS_THROUGH{{
969 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00
970 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10
971 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20
972 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, // 0x30
973 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
974 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 0x50
975 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
976 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 0x70
977 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80
978 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90
979 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0
980 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0
981 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0
982 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0
983 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0
984 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0
985 }};
986
987 bool in_fragment{false};
988 // The square brackets are rare, so the IP literal is only located once
989 // one of them shows up
990 bool literal_located{false};
991 std::pair<std::size_t, std::size_t> literal{std::string_view::npos,
992 std::string_view::npos};
993 std::size_t run_start{0};
994 for (std::size_t position = 0; position < input.size(); position += 1) {
995 const auto byte{static_cast<unsigned char>(input[position])};
996 // RFC 3986 Section 2.1: pct-encoded = "%" HEXDIG HEXDIG
997 if (PASS_THROUGH[byte] != 0 ||
998 (byte == '%' && is_percent_triplet(input, position))) {
999 continue;
1000 }
1001
1002 // RFC 3986 Section 3.5: "A fragment identifier component is indicated
1003 // by the presence of a number sign ("#") character and terminated by
1004 // the end of the URI", where fragment = *( pchar / "/" / "?" ) leaves no
1005 // room for another number sign
1006 if (byte == '#' && !in_fragment) {
1007 in_fragment = true;
1008 continue;
1009 }
1010
1011 if (byte == '[' || byte == ']') [[unlikely]] {
1012 if (!literal_located) {
1013 literal = ip_literal_brackets(input);
1014 literal_located = true;
1015 }
1016
1017 if (position == literal.first || position == literal.second) {
1018 continue;
1019 }
1020 }
1021
1022 // The characters that pass through are appended in runs
1023 output.append(input.substr(run_start, position - run_start));
1024 // RFC 3986 Section 2.1: percent-encode with uppercase hexadecimal
1025 const auto high{static_cast<unsigned char>((byte >> 4U) & 0x0FU)};
1026 const auto low{static_cast<unsigned char>(byte & 0x0FU)};
1027 const std::array<char, 3> encoded{
1028 {'%', static_cast<char>(high < 10 ? '0' + high : 'A' + high - 10),
1029 static_cast<char>(low < 10 ? '0' + low : 'A' + low - 10)}};
1030 output.append(std::string_view{encoded.data(), encoded.size()});
1031 run_start = position + 1;
1032 }
1033
1034 output.append(input.substr(run_start));
1035 }
1036
1057 template <typename Output>
1058 static auto append_query_parameter(Output &sink, const std::string_view name,
1059 const std::string_view value,
1060 const char opener = '?') -> void {
1061 if (!sink.empty() && sink.back() != opener && sink.back() != '&') {
1062 sink.push_back('&');
1063 }
1064
1065 URI::escape(name, sink);
1066 sink.push_back('=');
1067 URI::escape(value, sink);
1068 }
1069
1080 [[nodiscard]] static auto unescape(std::string_view input) -> std::string;
1081
1100 template <typename Output>
1101 [[nodiscard]] static auto unescape_form(const std::string_view input,
1102 Output &output) -> bool {
1103 const auto base{output.size()};
1104 output.reserve(base + input.size());
1105 for (std::size_t position = 0; position < input.size();) {
1106 const auto character{input[position]};
1107 if (character == '+') {
1108 output.push_back(' ');
1109 position += 1;
1110 } else if (character == '%') {
1111 const auto high{position + 2 < input.size()
1112 ? hex_digit_value(input[position + 1])
1113 : static_cast<std::int8_t>(-1)};
1114 const auto low{high < 0 ? static_cast<std::int8_t>(-1)
1115 : hex_digit_value(input[position + 2])};
1116 if (low < 0) {
1117 output.resize(base, '\0');
1118 return false;
1119 }
1120
1121 output.push_back(static_cast<char>((high << 4) | low));
1122 position += 3;
1123 } else {
1124 output.push_back(character);
1125 position += 1;
1126 }
1127 }
1128
1129 return true;
1130 }
1131
1148 template <typename Output>
1149 static auto escape_form(const std::string_view input, Output &output)
1150 -> void {
1151 output.reserve(output.size() + (input.size() * 3));
1152 for (const auto character : input) {
1153 const auto byte{static_cast<unsigned char>(character)};
1154 // WHATWG URL Section 5.2 percent-encodes with the space as plus flag set
1155 if (byte == ' ') {
1156 output.push_back('+');
1157 continue;
1158 }
1159
1160 // WHATWG URL Section 1.3: "The application/x-www-form-urlencoded
1161 // percent-encode set contains all code points, except the ASCII
1162 // alphanumeric, U+002A (*), U+002D (-), U+002E (.), and U+005F (_)"
1163 if (is_alphanum(character) || byte == '*' || byte == '-' || byte == '.' ||
1164 byte == '_') {
1165 output.push_back(character);
1166 continue;
1167 }
1168
1169 // RFC 3986 Section 2.1: percent-encode with uppercase hexadecimal
1170 const auto high{static_cast<unsigned char>((byte >> 4U) & 0x0FU)};
1171 const auto low{static_cast<unsigned char>(byte & 0x0FU)};
1172 output.push_back('%');
1173 output.push_back(
1174 static_cast<char>(high < 10 ? '0' + high : 'A' + high - 10));
1175 output.push_back(
1176 static_cast<char>(low < 10 ? '0' + low : 'A' + low - 10));
1177 }
1178 }
1179
1197 template <typename Output>
1198 static auto append_form_parameter(Output &sink, const std::string_view name,
1199 const std::string_view value) -> void {
1200 // WHATWG URL Section 5.2: "If output is not the empty string, then append
1201 // U+0026 (&) to output"
1202 if (!sink.empty()) {
1203 sink.push_back('&');
1204 }
1205
1206 URI::escape_form(name, sink);
1207 sink.push_back('=');
1208 URI::escape_form(value, sink);
1209 }
1210
1232 template <typename Container>
1233 requires requires(Container container, Container::value_type entry) {
1234 container.emplace_back();
1235 entry.first.push_back('\0');
1236 entry.second.push_back('\0');
1237 }
1238 static auto parse_form(const std::string_view input, Container &output)
1239 -> void {
1240 std::size_t position{0};
1241 while (position <= input.size()) {
1242 const auto separator{input.find('&', position)};
1243 const auto end{separator == std::string_view::npos ? input.size()
1244 : separator};
1245 const auto sequence{input.substr(position, end - position)};
1246 position = end + 1;
1247
1248 // WHATWG URL Section 5.1: "If bytes is the empty byte sequence, then
1249 // continue"
1250 if (sequence.empty()) {
1251 continue;
1252 }
1253
1254 // WHATWG URL Section 5.1: "If bytes contains a 0x3D (=), then let name
1255 // be the bytes from the start of bytes up to but excluding its first
1256 // 0x3D (=), and let value be the bytes, if any, after the first 0x3D
1257 // (=) up to the end of bytes"
1258 const auto equals{sequence.find('=')};
1259 auto &entry{output.emplace_back()};
1260 if (equals == std::string_view::npos) {
1261 URI::decode_form_component(sequence, entry.first);
1262 } else {
1263 URI::decode_form_component(sequence.substr(0, equals), entry.first);
1264 URI::decode_form_component(sequence.substr(equals + 1), entry.second);
1265 }
1266 }
1267 }
1268
1279 [[nodiscard]] static auto normalize_path(std::string_view path)
1280 -> std::string;
1281
1293 [[nodiscard]] static auto is_scheme(std::string_view input) noexcept -> bool;
1294
1305 [[nodiscard]] static auto is_gen_delim(char character) noexcept -> bool;
1306
1320 [[nodiscard]] static auto is_pchar(char character) noexcept -> bool;
1321
1333 [[nodiscard]] static auto is_uri(std::string_view input) noexcept -> bool;
1334
1347 [[nodiscard]] static auto is_uri_reference(std::string_view input) noexcept
1348 -> bool;
1349
1377 [[nodiscard]] static auto
1378 is_absolute_path_reference(std::string_view input) noexcept -> bool;
1379
1390 [[nodiscard]] static auto is_iri(std::string_view input) noexcept -> bool;
1391
1404 [[nodiscard]] static auto is_iri_reference(std::string_view input) noexcept
1405 -> bool;
1406
1418 [[nodiscard]] static auto strip_path_prefix(std::string_view path,
1419 std::string_view prefix)
1420 -> std::optional<std::string>;
1421
1433 [[nodiscard]] static auto rebase_path(std::string_view path,
1434 std::string_view old_prefix,
1435 std::string_view new_prefix)
1436 -> std::optional<std::string>;
1437
1438private:
1439 auto parse(std::string_view input) -> void;
1440
1441 // The positions of the square brackets around the IP literal host of a URI
1442 // reference, if it has one
1443 [[nodiscard]] static auto ip_literal_brackets(std::string_view input) noexcept
1444 -> std::pair<std::size_t, std::size_t>;
1445
1446 // WHATWG URL Section 5.1: "Replace any 0x2B (+) in name and value with 0x20
1447 // (SP)" and then percent-decode, which Section 1.3 defines to append a "%"
1448 // that is not followed by two hexadecimal digits as it appears rather than
1449 // to fail
1450 template <typename Output>
1451 static auto decode_form_component(const std::string_view input,
1452 Output &output) -> void {
1453 output.reserve(output.size() + input.size());
1454 for (std::size_t position = 0; position < input.size();) {
1455 const auto character{input[position]};
1456 if (character == '+') {
1457 output.push_back(' ');
1458 position += 1;
1459 continue;
1460 }
1461
1462 if (character == '%' && position + 2 < input.size()) {
1463 const auto high{hex_digit_value(input[position + 1])};
1464 const auto low{high < 0 ? static_cast<std::int8_t>(-1)
1465 : hex_digit_value(input[position + 2])};
1466 if (low >= 0) {
1467 output.push_back(static_cast<char>((high << 4) | low));
1468 position += 3;
1469 continue;
1470 }
1471 }
1472
1473 output.push_back(character);
1474 position += 1;
1475 }
1476 }
1477
1478// Exporting symbols that depends on the standard C++ library is considered
1479// safe.
1480// https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4275?view=msvc-170&redirectedfrom=MSDN
1481#if defined(_MSC_VER)
1482#pragma warning(push)
1483#pragma warning(disable : 4251)
1484#endif
1485 std::optional<std::string> path_{};
1486 std::optional<std::string> userinfo_{};
1487 std::optional<std::string> host_{};
1488 std::optional<std::uint32_t> port_{};
1489 std::optional<std::string> scheme_{};
1490 std::optional<std::string> fragment_{};
1491 std::optional<std::string> query_{};
1492 bool ip_literal_{false};
1493 // Whether this object was parsed as an IRI (RFC 3987) rather than a URI
1494 // (RFC 3986)
1495 bool iri_{false};
1496#if defined(_MSC_VER)
1497#pragma warning(pop)
1498#endif
1499};
1500
1501} // namespace sourcemeta::core
1502
1503#endif
Definition uri.h:468
auto at(const std::string_view name) const -> std::optional< std::string_view >
auto raw() const -> std::string_view
Query(const std::string_view raw)
constexpr auto is_alphanum(const Character character) noexcept -> bool
Definition text.h:288
auto hex_digit_value(const char character) noexcept -> std::int8_t
Definition text.h:850
auto is_percent_triplet(const std::string_view input, const std::size_t position) noexcept -> bool
Definition text.h:901
static auto unescape_form(const std::string_view input, Output &output) -> bool
Definition uri.h:1101
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 escape_reference(const std::string_view input, Output &output) -> void
Definition uri.h:963
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
static auto from_unescaped_fragment(const std::string_view fragment) -> URI
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_pchar(char character) noexcept -> bool
URI(std::istream &input)
static auto is_absolute_path_reference(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 unescaped_fragment(const std::string_view fragment) -> URI &
static auto append_form_parameter(Output &sink, const std::string_view name, const std::string_view value) -> void
Definition uri.h:1198
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:1058
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
static auto escape_form(const std::string_view input, Output &output) -> void
Definition uri.h:1149
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 parse_form(const std::string_view input, Container &output) -> void
Definition uri.h:1238
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