HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
URI.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
9#pragma once
10
11#include "../cast.hpp"
12#include "../exception.hpp"
13#include "../ranges.hpp"
14#include "../hash.hpp"
15#include "../strings.hpp"
16#include "../concepts.hpp"
17#include "../charconv.hpp"
18#include <string>
19#include <optional>
20#include <ranges>
21
22namespace hi { inline namespace v1 {
23
24#define HI_SUB_DELIM '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '='
25#define HI_PCHAR HI_SUB_DELIM, ':', '@'
26
41class URI {
42private:
43 using const_iterator = std::string_view::const_iterator;
44
45public:
47 public:
48 constexpr authority_type() noexcept = default;
49 constexpr authority_type(authority_type const&) noexcept = default;
50 constexpr authority_type(authority_type&&) noexcept = default;
51 constexpr authority_type& operator=(authority_type const&) noexcept = default;
52 constexpr authority_type& operator=(authority_type&&) noexcept = default;
53
54 constexpr authority_type(std::string_view const& rhs) noexcept
55 {
56 parse(rhs);
57 }
58
59 // constexpr void normalize(std::optional<std::string> const& scheme) noexcept
60 //{
61 // hi_not_implemented();
62 // }
63
64 [[nodiscard]] constexpr std::optional<std::string> const& userinfo() const noexcept
65 {
66 return _userinfo;
67 }
68
69 constexpr authority_type& set_userinfo(std::optional<std::string> const& rhs) noexcept
70 {
71 _userinfo = rhs;
72 return *this;
73 }
74
75 [[nodiscard]] constexpr std::string const& host() const noexcept
76 {
77 return _host;
78 }
79
80 constexpr authority_type& set_host(std::string const& rhs)
81 {
82 validate_host(rhs);
83 _host = to_lower(rhs);
84 return *this;
85 }
86
87 [[nodiscard]] constexpr std::optional<std::string> const& port() const noexcept
88 {
89 return _port;
90 }
91
92 constexpr authority_type& set_port(std::optional<std::string> const& rhs)
93 {
94 if (rhs) {
95 validate_port(*rhs);
96 _port = *rhs;
97 } else {
98 _port = {};
99 }
100 return *this;
101 }
102
103 [[nodiscard]] constexpr friend bool operator==(authority_type const&, authority_type const&) noexcept = default;
104 [[nodiscard]] constexpr friend auto operator<=>(authority_type const&, authority_type const&) noexcept = default;
105
106 [[nodiscard]] constexpr friend size_t to_string_size(authority_type const& rhs) noexcept
107 {
108 auto size = 0_uz;
109 if (rhs._userinfo) {
110 size += rhs._userinfo->size() + 1;
111 }
112 size += rhs._host.size();
113 if (rhs._port) {
114 size += rhs._port->size() + 1;
115 }
116 return size;
117 }
118
119 [[nodiscard]] constexpr friend std::string to_string(authority_type const& rhs) noexcept
120 {
121 auto r = std::string{};
122
123 if (rhs._userinfo) {
124 r += URI::encode<HI_SUB_DELIM, ':'>(*rhs._userinfo);
125 r += '@';
126 }
127
128 if (rhs._host.empty() or rhs._host.front() != '[') {
129 r += URI::encode<HI_SUB_DELIM>(rhs._host);
130 } else {
131 r += URI::encode<HI_SUB_DELIM, '[', ']', ':'>(rhs._host);
132 }
133
134 if (rhs._port) {
135 r += ':';
136 r += *rhs._port;
137 }
138
139 return r;
140 }
141
142 private:
143 std::optional<std::string> _userinfo = {};
144 std::string _host = {};
145 std::optional<std::string> _port = {};
146
147 [[nodiscard]] constexpr static void validate_host(std::string_view str)
148 {
149 if (str.starts_with('[') and not str.ends_with(']')) {
150 throw uri_error("The host-component starts with '[' has missing ']' at end");
151 } else if (str.ends_with(']') and str.starts_with('[')) {
152 throw uri_error("The host-component ends with ']' has missing '[' at start");
153 }
154 }
155
156 [[nodiscard]] constexpr static void validate_port(std::string_view str)
157 {
158 for (auto c : str) {
159 if (not(c >= '0' and c <= '9')) {
160 throw uri_error("The port-component contains a non-digit.");
161 }
162 }
163 }
164
172 [[nodiscard]] constexpr const_iterator parse_userinfo(const_iterator first, const_iterator last) noexcept
173 {
174 for (auto it = first; it != last; ++it) {
175 if (hilet c = *it; c == '@') {
176 set_userinfo(URI::decode(first, it));
177 return it + 1; // Skip over '@'.
178 }
179 }
180
181 // Reached at end of URI, this is not a userinfo.
182 return first;
183 }
184
191 [[nodiscard]] constexpr const_iterator parse_host(const_iterator first, const_iterator last) noexcept
192 {
193 if (first == last) {
194 return first;
195 }
196
197 auto it = first;
198 if (*it == '[') {
199 while (it != last) {
200 if (*it == ']') {
201 set_host(URI::decode(first, it + 1));
202 return it + 1; // Skip over ']'.
203 }
204 }
205 // End of URI mean this is not a host, interpret as path instead.
206 return first;
207
208 } else {
209 for (; it != last; ++it) {
210 if (hilet c = *it; c == ':') {
211 // End of host.
212 set_host(URI::decode(first, it));
213 return it;
214 }
215 }
216
217 // End of URI means that the host is the last part of the URI
218 set_host(URI::decode(first, last));
219 return last;
220 }
221 }
222
223 [[nodiscard]] constexpr const_iterator parse_port(const_iterator first, const_iterator last) noexcept
224 {
225 set_port(std::string{first, last});
226 return last;
227 }
228
229 constexpr void parse(std::string_view rhs) noexcept
230 {
231 auto first = rhs.cbegin();
232 auto last = rhs.cend();
233 auto it = parse_userinfo(first, last);
234 it = parse_host(it, last);
235
236 if (it != last and *it == ':') {
237 it = parse_port(++it, last);
238 }
239 }
240 };
241
272 class path_type : public std::vector<std::string> {
273 public:
274 constexpr path_type() noexcept = default;
275 constexpr path_type(path_type const&) noexcept = default;
276 constexpr path_type(path_type&&) noexcept = default;
277 constexpr path_type& operator=(path_type const&) noexcept = default;
278 constexpr path_type& operator=(path_type&&) noexcept = default;
279
280 [[nodiscard]] constexpr static std::vector<std::string> parse(std::string_view str)
281 {
282 auto r = make_vector<std::string>(std::views::transform(std::views::split(str, std::string_view{"/"}), [](auto&& x) {
283 return URI::decode(std::string_view{std::ranges::begin(x), std::ranges::end(x)});
284 }));
285
286 if (r.size() == 1 and r.front().empty()) {
287 // An empty string will evaluate to a single segment.
288 r.clear();
289 }
290 if (not r.empty() and (r.back() == "." or r.back() == ".." or r.back() == "**")) {
291 // ".", ".." and "**" are directories always terminate with a slash.
292 r.emplace_back();
293 }
294
295 return r;
296 }
297
298 constexpr path_type(std::string_view str) noexcept : std::vector<std::string>(parse(str))
299 {
300 hi_axiom(holds_invariant());
301 }
302
303 [[nodiscard]] constexpr bool absolute() const noexcept
304 {
305 return size() >= 2 and front().empty();
306 }
307
308 [[nodiscard]] constexpr bool double_absolute() const noexcept
309 {
310 return size() >= 3 and (*this)[0].empty() and (*this)[1].empty();
311 }
312
313 [[nodiscard]] constexpr bool is_root() const noexcept
314 {
315 return size() == 2 and (*this)[0].empty() and (*this)[1].empty();
316 }
317
318 [[nodiscard]] constexpr std::optional<std::string> filename() const noexcept
319 {
320 if (empty() or back().empty()) {
321 return {};
322 } else {
323 return back();
324 }
325 }
326
332 constexpr path_type& remove_filename() noexcept
333 {
334 switch (size()) {
335 case 0: // Don't remove a filename from an empty path.
336 break;
337 case 1: // relative filename, make the path empty.
338 clear();
339 break;
340 default: // relative or absolute directory with optional filename. Just empty the last path segment.
341 back().clear();
342 }
343 hi_axiom(holds_invariant());
344 return *this;
345 }
346
347 [[nodiscard]] constexpr friend path_type merge(path_type base, path_type const& ref, bool base_has_authority) noexcept
348 {
349 if (base_has_authority and base.empty()) {
350 // A URL with an authority and empty path is implicitly the root path.
351 base.emplace_back();
352 base.emplace_back();
353 }
354
355 if (not base.empty()) {
356 // Remove the (possibly empty) filename from the base.
357 base.pop_back();
358 }
359 base.insert(base.cend(), ref.cbegin(), ref.cend());
360
361 if (base.size() == 1 and base.front().empty()) {
362 // Empty ref-path added to root base-path, fix by appending an empty filename.
363 base.emplace_back();
364 }
365
366 hi_axiom(base.holds_invariant());
367 return base;
368 }
369
395 [[nodiscard]] constexpr friend path_type remove_dot_segments(path_type path) noexcept
396 {
397 for (auto it = path.cbegin(); it != path.cend();) {
398 if (*it == ".") {
399 // Remove any "." from the path.
400 it = path.erase(it);
401
402 } else if (*it == "..") {
403 if (it == path.cbegin()) {
404 // Remove the ".." at the start of a relative path.
405 it = path.erase(it);
406
407 } else if (it - 1 == path.cbegin() and (it - 1)->empty()) {
408 // Remove just the ".." at the start of an absolute path.
409 it = path.erase(it);
410
411 } else {
412 // Remove ".." and the segment in front of it.
413 it = path.erase(it - 1, it + 1);
414 }
415 } else {
416 // Ignore other segments.
417 ++it;
418 }
419 }
420
421 if (path.size() == 1 and path.front().empty()) {
422 // After removing the ".." at the start of the path we are left with an empty segment.
423 path.clear();
424 }
425
426 hi_axiom(path.holds_invariant());
427 return path;
428 }
429
430 [[nodiscard]] constexpr bool holds_invariant() const noexcept
431 {
432 if (empty()) {
433 return true;
434 }
435
436 if (back() == "." or back() == ".." or back() == "**") {
437 // ".", ".." and "**" are always directories and may not be the last segment.
438 return false;
439 }
440 return true;
441 }
442
443 [[nodiscard]] constexpr friend size_t to_string_size(path_type const& rhs) noexcept
444 {
445 size_t r = 0_uz;
446 for (hilet& segment : rhs) {
447 r += segment.size();
448 }
449 r += rhs.size() + 1;
450 return r;
451 }
452
459 [[nodiscard]] constexpr friend std::string to_string(path_type const& rhs, bool has_scheme = false) noexcept
460 {
461 auto r = std::string{};
462 r.reserve(to_string_size(rhs));
463
464 auto segment_is_empty = false;
465 for (auto it = rhs.cbegin(); it != rhs.cend(); ++it) {
466 segment_is_empty = it->empty();
467
468 if (it == rhs.cbegin() and not has_scheme) {
469 r += URI::encode<HI_SUB_DELIM, '@'>(*it);
470 } else {
471 r += URI::encode<HI_PCHAR>(*it);
472 }
473 r += '/';
474 }
475
476 if (not r.empty() and not segment_is_empty) {
477 // The last path-component was a filename, remove the trailing slash '/'.
478 r.pop_back();
479 }
480 return r;
481 }
482 };
483
484 constexpr URI() noexcept = default;
485 constexpr URI(URI const&) noexcept = default;
486 constexpr URI(URI&&) noexcept = default;
487 constexpr URI& operator=(URI const&) noexcept = default;
488 constexpr URI& operator=(URI&&) noexcept = default;
489
495 constexpr explicit URI(std::string_view str)
496 {
497 parse(str);
498 }
499
500 constexpr explicit URI(std::string const& str) : URI(std::string_view{str}) {}
501
502 constexpr explicit URI(const char *str) : URI(std::string_view{str}) {}
503
504 [[nodiscard]] constexpr bool empty() const noexcept
505 {
506 return not _scheme and not _authority and _path.empty() and not _query and not _fragment;
507 }
508
509 constexpr operator bool() const noexcept
510 {
511 return not empty();
512 }
513
518 [[nodiscard]] constexpr std::optional<std::string> const& scheme() const noexcept
519 {
520 return _scheme;
521 }
522
526 constexpr URI& set_scheme(std::optional<std::string> const& rhs)
527 {
528 if (rhs) {
529 validate_scheme(*rhs);
530 _scheme = *rhs;
531 } else {
532 _scheme = {};
533 }
534 return *this;
535 }
536
541 [[nodiscard]] constexpr std::optional<authority_type> const& authority() const noexcept
542 {
543 return _authority;
544 }
545
546 constexpr URI& set_authority(std::optional<authority_type> const& rhs) noexcept
547 {
548 _authority = rhs;
549 return *this;
550 }
551
552 [[nodiscard]] constexpr path_type const& path() const noexcept
553 {
554 return _path;
555 }
556
557 constexpr URI& set_path(path_type const& rhs)
558 {
559 validate_path(rhs, to_bool(_authority));
560 _path = rhs;
561 return *this;
562 }
563
564 [[nodiscard]] constexpr std::optional<std::string> filename() const noexcept
565 {
566 return _path.filename();
567 }
568
574 constexpr URI& remove_filename() noexcept
575 {
576 _path.remove_filename();
577 return *this;
578 }
579
584 [[nodiscard]] constexpr std::optional<std::string> const& query() const noexcept
585 {
586 return _query;
587 }
588
589 constexpr URI& set_query(std::optional<std::string> const& rhs) noexcept
590 {
591 _query = rhs;
592 return *this;
593 }
594
599 [[nodiscard]] constexpr std::optional<std::string> const& fragment() const noexcept
600 {
601 return _fragment;
602 }
603
604 constexpr URI& set_fragment(std::optional<std::string> const& rhs) noexcept
605 {
606 _fragment = rhs;
607 return *this;
608 }
609
610 [[nodiscard]] constexpr friend std::string to_string(URI const& rhs) noexcept
611 {
612 auto r = std::string{};
613 r.reserve(to_string_size(rhs));
614
615 if (rhs._scheme) {
616 r += *rhs._scheme;
617 r += ':';
618 }
619
620 if (rhs._authority) {
621 r += '/';
622 r += '/';
623 r += to_string(*rhs._authority);
624 }
625
626 r += to_string(rhs._path, to_bool(rhs._scheme));
627
628 if (rhs._query) {
629 r += '?';
630 r += URI::encode<HI_PCHAR, '/', '?'>(*rhs._query);
631 }
632
633 if (rhs._fragment) {
634 r += '#';
635 r += URI::encode<HI_PCHAR, '/', '?'>(*rhs._fragment);
636 }
637
638 return r;
639 }
640
641 friend std::ostream& operator<<(std::ostream& lhs, URI const& rhs) noexcept
642 {
643 return lhs << to_string(rhs);
644 }
645
646 [[nodiscard]] constexpr friend bool operator==(URI const& lhs, URI const& rhs) noexcept = default;
647 [[nodiscard]] constexpr friend auto operator<=>(URI const& lhs, URI const& rhs) noexcept = default;
648
649 [[nodiscard]] constexpr friend URI operator/(URI const& base, URI const& ref) noexcept
650 {
651 auto target = URI{};
652
653 if (ref._scheme) {
654 target._scheme = ref._scheme;
655 target._authority = ref._authority;
656 target._path = remove_dot_segments(ref._path);
657 target._query = ref._query;
658 } else {
659 if (ref._authority) {
660 target._authority = ref._authority;
661 target._path = remove_dot_segments(ref._path);
662 target._query = ref._query;
663
664 } else {
665 if (ref._path.empty()) {
666 target._path = base._path;
667 if (ref._query) {
668 target._query = ref._query;
669 } else {
670 target._query = base._query;
671 }
672 } else {
673 if (ref._path.absolute()) {
674 target._path = remove_dot_segments(ref._path);
675 } else {
676 target._path = remove_dot_segments(merge(base._path, ref._path, to_bool(base._authority)));
677 }
678 target._query = ref._query;
679 }
680 target._authority = base._authority;
681 }
682 target._scheme = base._scheme;
683 }
684
685 target._fragment = ref._fragment;
686
687 return target;
688 }
689
690 [[nodiscard]] constexpr friend bool operator==(URI const& lhs, std::string_view rhs) noexcept
691 {
692 return lhs == URI(rhs);
693 }
694
695 [[nodiscard]] constexpr friend auto operator<=>(URI const& lhs, std::string_view rhs) noexcept
696 {
697 return lhs == URI(rhs);
698 }
699
700 [[nodiscard]] constexpr friend URI operator/(URI const& base, std::string_view ref) noexcept
701 {
702 return base / URI(ref);
703 }
704
710 [[nodiscard]] constexpr static std::string decode(std::string_view rhs)
711 {
712 auto r = std::string{rhs};
713
714 for (auto i = r.find('%'); i != std::string::npos; i = r.find('%', i)) {
715 // This may throw a parse_error, if not hexadecimal
716 auto c = from_string<uint8_t>(r.substr(i + 1, 2), 16);
717
718 // Replace the % encoded character.
719 r.replace(i, 3, 1, char_cast<char>(c));
720
721 // Skip over encoded-character.
722 ++i;
723 }
724
725 return r;
726 }
727
734 [[nodiscard]] constexpr static std::string decode(const_iterator first, const_iterator last)
735 {
736 return decode(std::string_view{first, last});
737 }
738
746 template<char... Extras, typename It, typename ItEnd>
747 [[nodiscard]] constexpr static std::string encode(It first, ItEnd last) noexcept
748 {
749 auto r = std::string{};
750 if constexpr (requires { std::distance(first, last); }) {
751 r.reserve(std::distance(first, last));
752 }
753
754 for (auto it = first; it != last; ++it) {
755 hilet c = *it;
756 // clang-format off
757 if (((
758 (c >= 'a' and c <= 'z') or
759 (c >= 'A' and c <= 'Z') or
760 (c >= '0' and c <= '9') or
761 c == '-' or c == '.' or c == '_' or c == '~'
762 ) or ... or (c == Extras))) {
763 r += c;
764
765 } else {
766 r += '%';
767 auto uc = char_cast<uint8_t>(c);
768 if (auto nibble = narrow_cast<char>(uc >> 4); nibble <= 9) {
769 r += '0' + nibble;
770 } else {
771 r += 'A' + nibble - 10;
772 }
773 if (auto nibble = narrow_cast<char>(uc & 0xf); nibble <= 9) {
774 r += '0' + nibble;
775 } else {
776 r += 'A' + nibble - 10;
777 }
778 }
779 // clang-format on
780 }
781
782 return r;
783 }
784
791 template<char... Extras, typename Range>
792 [[nodiscard]] constexpr static std::string encode(Range&& range) noexcept
793 {
794 return encode<Extras...>(std::ranges::begin(range), std::ranges::end(range));
795 }
796
797private:
798 std::optional<std::string> _scheme;
799 std::optional<authority_type> _authority;
800 path_type _path;
801 std::optional<std::string> _query;
802 std::optional<std::string> _fragment;
803
804 [[nodiscard]] constexpr friend size_t to_string_size(URI const& rhs) noexcept
805 {
806 auto size = 0_uz;
807 if (rhs._scheme) {
808 size += rhs._scheme->size() + 1;
809 }
810 if (rhs._authority) {
811 size += to_string_size(*rhs._authority) + 2;
812 }
813 size += to_string_size(rhs._path);
814
815 if (rhs._query) {
816 size += rhs._query->size() + 1;
817 }
818 if (rhs._fragment) {
819 size += rhs._fragment->size() + 1;
820 }
821
822 return size;
823 }
824
825 [[nodiscard]] constexpr static bool is_scheme_start(char c) noexcept
826 {
827 return (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z');
828 }
829
830 [[nodiscard]] constexpr static bool is_scheme(char c) noexcept
831 {
832 return is_scheme_start(c) or (c >= '0' and c <= '9') or c == '+' or c == '-' or c == '.';
833 }
834
835 [[nodiscard]] constexpr static void validate_scheme(std::string_view str)
836 {
837 if (str.empty()) {
838 throw uri_error("The scheme-component is not allowed to be empty (it is allowed to not exist).");
839 }
840 if (not is_scheme_start(str.front())) {
841 throw uri_error("The scheme-component does not start with [a-zA-Z].");
842 }
843 for (auto c : str) {
844 if (not is_scheme(c)) {
845 throw uri_error("The scheme-component contains a character outside [a-zA-Z0-9.+-].");
846 }
847 }
848 }
849
850 [[nodiscard]] constexpr static void validate_path(path_type const& path, bool has_authority)
851 {
852 if (has_authority) {
853 if (not(path.empty() or path.absolute())) {
854 throw uri_error("A path-component in a URI with an authority-component must be empty or absolute.");
855 }
856 } else if (path.double_absolute()) {
857 throw uri_error("A path-component in a URI without an authority-component may not start with a double slash '//'.");
858 }
859 }
860
867 [[nodiscard]] constexpr const_iterator parse_scheme(const_iterator first, const_iterator last)
868 {
869 for (auto it = first; it != last; ++it) {
870 if (hilet c = *it; c == ':') {
871 set_scheme(std::string{first, it});
872 return it + 1; // Skip over ':'.
873
874 } else if (c == '/' or c == '?' or c == '#') {
875 // Invalid character, this is not a scheme.
876 return first;
877 }
878 }
879
880 // Reached at end of URI, this is not a scheme.
881 return first;
882 }
883
884 [[nodiscard]] constexpr const_iterator parse_authority(const_iterator first, const_iterator last)
885 {
886 for (auto it = first; it != last; it++) {
887 if (hilet c = *it; c == '/' or c == '?' or c == '#') {
888 set_authority(authority_type{std::string_view{first, it}});
889 return it;
890 }
891 }
892
893 set_authority(authority_type{std::string_view{first, last}});
894 return last;
895 }
896
897 [[nodiscard]] constexpr const_iterator parse_path(const_iterator first, const_iterator last)
898 {
899 for (auto it = first; it != last; it++) {
900 if (hilet c = *it; c == '?' or c == '#') {
901 set_path(path_type{std::string_view{first, it}});
902 return it;
903 }
904 }
905
906 set_path(path_type{std::string_view{first, last}});
907 return last;
908 }
909
910 [[nodiscard]] constexpr const_iterator parse_query(const_iterator first, const_iterator last)
911 {
912 for (auto it = first; it != last; it++) {
913 if (hilet c = *it; c == '#') {
914 set_query(URI::decode(first, it));
915 return it;
916 }
917 }
918
919 set_query(URI::decode(first, last));
920 return last;
921 }
922
923 [[nodiscard]] constexpr const_iterator parse_fragment(const_iterator first, const_iterator last)
924 {
925 set_fragment(URI::decode(first, last));
926 return last;
927 }
928
929 constexpr void parse(std::string_view str)
930 {
931 auto first = str.cbegin();
932 auto last = str.cend();
933 auto it = parse_scheme(first, last);
934
935 if (std::distance(it, last) >= 2 and it[0] == '/' and it[1] == '/') {
936 it += 2;
937 it = parse_authority(it, last);
938 }
939
940 it = parse_path(it, last);
941
942 if (it != last and *it == '?') {
943 it = parse_query(++it, last);
944 }
945
946 if (it != last and *it == '#') {
947 it = parse_fragment(++it, last);
948 }
949 }
950
951 friend struct std::hash<URI>;
952};
953
954#undef HI_PCHAR
955#undef HI_SUB_DELIM
956
957}} // namespace hi::v1
958
959template<>
960struct std::hash<hi::URI> {
961 [[nodiscard]] size_t operator()(hi::URI const& rhs) const noexcept
962 {
963 return std::hash<std::string>{}(to_string(rhs));
964 }
965};
966
967template<typename CharT>
968struct std::formatter<hi::URI, CharT> : std::formatter<std::string, CharT> {
969 auto format(hi::URI const& t, auto& fc)
970 {
971 return std::formatter<std::string, CharT>::format(to_string(t), fc);
972 }
973};
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:15
The HikoGUI namespace.
Definition ascii.hpp:19
Definition exception.hpp:113
A Uniform Resource Identifier.
Definition URI.hpp:41
static constexpr std::string decode(std::string_view rhs)
URI percent-encoding decode function.
Definition URI.hpp:710
static constexpr std::string decode(const_iterator first, const_iterator last)
URI percent-encoding decode function.
Definition URI.hpp:734
constexpr std::optional< std::string > const & query() const noexcept
Get the query-component of the URI.
Definition URI.hpp:584
constexpr std::optional< std::string > const & fragment() const noexcept
Get the fragment-component of the URI.
Definition URI.hpp:599
static constexpr std::string encode(Range &&range) noexcept
URI encode a component.
Definition URI.hpp:792
static constexpr std::string encode(It first, ItEnd last) noexcept
URI encode a component.
Definition URI.hpp:747
constexpr std::optional< authority_type > const & authority() const noexcept
Get the authority-component of the URI.
Definition URI.hpp:541
constexpr URI & remove_filename() noexcept
Remove the filename part of the path.
Definition URI.hpp:574
constexpr std::optional< std::string > const & scheme() const noexcept
Get the scheme-component of the URI.
Definition URI.hpp:518
constexpr URI & set_scheme(std::optional< std::string > const &rhs)
Get the scheme-component of the URI.
Definition URI.hpp:526
Definition URI.hpp:46
A path type.
Definition URI.hpp:272
constexpr path_type & remove_filename() noexcept
Remove the filename part of the path.
Definition URI.hpp:332
constexpr friend std::string to_string(path_type const &rhs, bool has_scheme=false) noexcept
Convert the URI path component to a string.
Definition URI.hpp:459
constexpr friend path_type remove_dot_segments(path_type path) noexcept
Definition URI.hpp:395
std::string back(std::string ... args)
T cbegin(T... args)
std::string clear(std::string ... args)
T distance(T... args)
T emplace_back(T... args)
T empty(T... args)
T cend(T... args)
T erase(T... args)
std::string front(std::string ... args)
T merge(T... args)
T pop_back(T... args)
T ref(T... args)
T reserve(T... args)
std::string size(std::string ... args)
T to_string(T... args)