47 using const_iterator = std::string_view::const_iterator;
68 [[
nodiscard]]
constexpr std::optional<std::string>
const& userinfo()
const noexcept
73 constexpr authority_type& set_userinfo(std::optional<std::string>
const& rhs)
noexcept
87 _host = to_lower(rhs);
91 [[
nodiscard]]
constexpr std::optional<std::string>
const& port()
const noexcept
96 constexpr authority_type& set_port(std::optional<std::string>
const& rhs)
114 size += rhs._userinfo->size() + 1;
116 size += rhs._host.size();
118 size += rhs._port->size() + 1;
128 r += URI::encode<HI_SUB_DELIM, ':'>(*rhs._userinfo);
132 if (rhs._host.empty()
or rhs._host.front() !=
'[') {
133 r += URI::encode<HI_SUB_DELIM>(rhs._host);
135 r += URI::encode<HI_SUB_DELIM, '[', ']', ':'>(rhs._host);
147 std::optional<std::string> _userinfo = {};
149 std::optional<std::string> _port = {};
151 constexpr static void validate_host(std::string_view str)
153 if (str.starts_with(
'[')
and not str.ends_with(
']')) {
154 throw uri_error(
"The host-component starts with '[' has missing ']' at end");
155 }
else if (str.ends_with(
']')
and str.starts_with(
'[')) {
156 throw uri_error(
"The host-component ends with ']' has missing '[' at start");
160 constexpr static void validate_port(std::string_view str)
163 if (
not(c >=
'0' and c <=
'9')) {
164 throw uri_error(
"The port-component contains a non-digit.");
176 [[
nodiscard]]
constexpr const_iterator parse_userinfo(const_iterator first, const_iterator last)
noexcept
178 for (
auto it = first;
it != last; ++
it) {
179 if (
auto const c = *
it; c ==
'@') {
195 [[
nodiscard]]
constexpr const_iterator parse_host(const_iterator first, const_iterator last)
noexcept
213 for (;
it != last; ++
it) {
214 if (
auto const c = *
it; c ==
':') {
227 [[
nodiscard]]
constexpr const_iterator parse_port(const_iterator first, const_iterator last)
noexcept
233 constexpr void parse(std::string_view rhs)
noexcept
235 auto first = rhs.cbegin();
236 auto last = rhs.cend();
237 auto it = parse_userinfo(first, last);
238 it = parse_host(
it, last);
240 if (
it != last
and *
it ==
':') {
241 it = parse_port(++
it, last);
278 constexpr path_type()
noexcept =
default;
286 auto r =
make_vector<std::string>(std::views::transform(std::views::split(str, std::string_view{
"/"}), [](
auto&& x) {
287 return URI::decode(std::string_view{std::ranges::begin(x), std::ranges::end(x)});
290 if (r.size() == 1
and r.front().empty()) {
294 if (
not r.empty()
and (r.back() ==
"." or r.back() ==
".." or r.back() ==
"**")) {
304 hi_axiom(holds_invariant());
307 [[
nodiscard]]
constexpr bool absolute()
const noexcept
312 [[
nodiscard]]
constexpr bool double_absolute()
const noexcept
317 [[
nodiscard]]
constexpr bool is_root()
const noexcept
322 [[
nodiscard]]
constexpr std::optional<std::string> filename()
const noexcept
347 hi_axiom(holds_invariant());
359 if (
not base.empty()) {
363 base.insert(base.cend(), ref.cbegin(), ref.cend());
365 if (base.size() == 1
and base.front().empty()) {
370 hi_axiom(base.holds_invariant());
401 for (
auto it = path.cbegin();
it != path.cend();) {
406 }
else if (*
it ==
"..") {
407 if (
it == path.cbegin()) {
411 }
else if (
it - 1 == path.cbegin()
and (
it - 1)->empty()) {
417 it = path.erase(
it - 1,
it + 1);
425 if (path.size() == 1
and path.front().empty()) {
430 hi_axiom(path.holds_invariant());
447 [[
nodiscard]]
constexpr friend size_t to_string_size(path_type
const& rhs)
noexcept
450 for (
auto const&
segment : rhs) {
466 r.
reserve(to_string_size(rhs));
473 r += URI::encode<HI_SUB_DELIM, '@'>(*
it);
475 r += URI::encode<HI_PCHAR>(*
it);
519 constexpr explicit URI(
const char *str) :
URI(
std::string_view{str}) {}
546 validate_scheme(*rhs);
563 constexpr URI& set_authority(std::optional<authority_type>
const& rhs)
noexcept
574 constexpr URI& set_path(path_type
const& rhs)
576 validate_path(rhs, to_bool(_authority));
583 return _path.filename();
606 constexpr URI& set_query(std::optional<std::string>
const& rhs)
noexcept
621 constexpr URI& set_fragment(std::optional<std::string>
const& rhs)
noexcept
630 r.
reserve(to_string_size(rhs));
637 if (rhs._authority) {
640 r += to_string(*rhs._authority);
643 r += to_string(rhs._path, to_bool(rhs._scheme));
652 r +=
URI::encode<HI_PCHAR,
'/',
'?'>(*rhs._fragment);
660 return lhs << to_string(rhs);
663 [[
nodiscard]]
constexpr friend bool operator==(URI
const& lhs, URI
const& rhs)
noexcept =
default;
664 [[
nodiscard]]
constexpr friend auto operator<=>(URI
const& lhs, URI
const& rhs)
noexcept =
default;
666 [[
nodiscard]]
constexpr friend URI operator/(URI
const& base, URI
const& ref)
noexcept
671 target._scheme =
ref._scheme;
672 target._authority =
ref._authority;
673 target._path = remove_dot_segments(
ref._path);
674 target._query =
ref._query;
676 if (
ref._authority) {
677 target._authority =
ref._authority;
678 target._path = remove_dot_segments(
ref._path);
679 target._query =
ref._query;
682 if (
ref._path.empty()) {
683 target._path = base._path;
685 target._query =
ref._query;
687 target._query = base._query;
690 if (
ref._path.absolute()) {
691 target._path = remove_dot_segments(
ref._path);
693 target._path = remove_dot_segments(
merge(base._path,
ref._path, to_bool(base._authority)));
695 target._query =
ref._query;
697 target._authority = base._authority;
699 target._scheme = base._scheme;
702 target._fragment =
ref._fragment;
707 [[
nodiscard]]
constexpr friend bool operator==(URI
const& lhs, std::string_view rhs)
noexcept
709 return lhs == URI(rhs);
712 [[
nodiscard]]
constexpr friend auto operator<=>(URI
const& lhs, std::string_view rhs)
noexcept
714 return lhs == URI(rhs);
717 [[
nodiscard]]
constexpr friend URI operator/(URI
const& base, std::string_view ref)
noexcept
719 return base / URI(ref);
731 for (
auto i = r.find(
'%'); i != std::string::npos; i = r.find(
'%', i)) {
753 return decode(std::string_view{first, last});
763 template<
char...
Extras,
typename It,
typename ItEnd>
771 for (
auto it = first;
it != last; ++
it) {
775 (c >=
'a' and c <=
'z')
or
776 (c >=
'A' and c <=
'Z')
or
777 (c >=
'0' and c <=
'9')
or
778 c ==
'-' or c ==
'.' or c ==
'_' or c ==
'~'
815 std::optional<std::string> _scheme;
816 std::optional<authority_type> _authority;
818 std::optional<std::string> _query;
819 std::optional<std::string> _fragment;
821 [[
nodiscard]]
constexpr friend size_t to_string_size(
URI const& rhs)
noexcept
825 size += rhs._scheme->size() + 1;
827 if (rhs._authority) {
828 size += to_string_size(*rhs._authority) + 2;
830 size += to_string_size(rhs._path);
833 size += rhs._query->size() + 1;
836 size += rhs._fragment->size() + 1;
842 [[
nodiscard]]
constexpr static bool is_scheme_start(
char c)
noexcept
844 return (c >=
'a' and c <=
'z')
or (c >=
'A' and c <=
'Z');
847 [[
nodiscard]]
constexpr static bool is_scheme(
char c)
noexcept
849 return is_scheme_start(c)
or (c >=
'0' and c <=
'9')
or c == '+'
or c == '-'
or c == '.';
855 throw uri_error(
"The scheme-component is not allowed to be empty (it is allowed to not exist).");
857 if (
not is_scheme_start(str.front())) {
858 throw uri_error(
"The scheme-component does not start with [a-zA-Z].");
861 if (
not is_scheme(c)) {
862 throw uri_error(
"The scheme-component contains a character outside [a-zA-Z0-9.+-].");
867 constexpr static void validate_path(path_type
const& path,
bool has_authority)
870 if (
not(path.empty()
or path.absolute())) {
871 throw uri_error(
"A path-component in a URI with an authority-component must be empty or absolute.");
873 }
else if (path.double_absolute()) {
874 throw uri_error(
"A path-component in a URI without an authority-component may not start with a double slash '//'.");
884 [[
nodiscard]]
constexpr const_iterator parse_scheme(const_iterator first, const_iterator last)
886 for (
auto it = first;
it != last; ++
it) {
887 if (
auto const c = *
it; c ==
':') {
891 }
else if (c ==
'/' or c ==
'?' or c ==
'#') {
901 [[
nodiscard]]
constexpr const_iterator parse_authority(const_iterator first, const_iterator last)
903 for (
auto it = first;
it != last;
it++) {
904 if (
auto const c = *
it; c ==
'/' or c ==
'?' or c ==
'#') {
905 set_authority(authority_type{std::string_view{first,
it}});
910 set_authority(authority_type{std::string_view{first, last}});
914 [[
nodiscard]]
constexpr const_iterator parse_path(const_iterator first, const_iterator last)
916 for (
auto it = first;
it != last;
it++) {
917 if (
auto const c = *
it; c ==
'?' or c ==
'#') {
918 set_path(path_type{std::string_view{first,
it}});
923 set_path(path_type{std::string_view{first, last}});
927 [[
nodiscard]]
constexpr const_iterator parse_query(const_iterator first, const_iterator last)
929 for (
auto it = first;
it != last;
it++) {
930 if (
auto const c = *
it; c ==
'#') {
940 [[
nodiscard]]
constexpr const_iterator parse_fragment(const_iterator first, const_iterator last)
946 constexpr void parse(std::string_view str)
948 auto first = str.cbegin();
949 auto last = str.cend();
950 auto it = parse_scheme(first, last);
954 it = parse_authority(
it, last);
957 it = parse_path(
it, last);
959 if (
it != last
and *
it ==
'?') {
960 it = parse_query(++
it, last);
963 if (
it != last
and *
it ==
'#') {
964 it = parse_fragment(++
it, last);