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