HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
URI.hpp
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
5#pragma once
6
7#include "cast.hpp"
8#include "exception.hpp"
9#include "ranges.hpp"
10#include "hash.hpp"
11#include "strings.hpp"
12#include "concepts.hpp"
13#include <string>
14#include <optional>
15#include <ranges>
16
17namespace hi { inline namespace v1 {
18
19#define HI_SUB_DELIM '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '='
20#define HI_PCHAR HI_SUB_DELIM, ':', '@'
21
25class URI {
26public:
28 public:
29 constexpr authority_type() noexcept = default;
30 constexpr authority_type(authority_type const&) noexcept = default;
31 constexpr authority_type(authority_type&&) noexcept = default;
32 constexpr authority_type& operator=(authority_type const&) noexcept = default;
33 constexpr authority_type& operator=(authority_type&&) noexcept = default;
34
35 constexpr authority_type(std::string_view const& rhs) noexcept
36 {
37 parse(rhs);
38 }
39
40 // constexpr void normalize(std::optional<std::string> const& scheme) noexcept
41 //{
42 // hi_not_implemented();
43 // }
44
45 [[nodiscard]] constexpr std::optional<std::string> const& userinfo() const noexcept
46 {
47 return _userinfo;
48 }
49
50 constexpr authority_type& set_userinfo(std::optional<std::string> const& rhs) noexcept
51 {
52 _userinfo = rhs;
53 return *this;
54 }
55
56 [[nodiscard]] constexpr std::string const& host() const noexcept
57 {
58 return _host;
59 }
60
61 constexpr authority_type& set_host(std::string const& rhs)
62 {
63 validate_host(rhs);
64 _host = to_lower(rhs);
65 return *this;
66 }
67
68 [[nodiscard]] constexpr std::optional<std::string> const& port() const noexcept
69 {
70 return _port;
71 }
72
73 constexpr authority_type& set_port(std::optional<std::string> const& rhs)
74 {
75 if (rhs) {
76 validate_port(*rhs);
77 _port = *rhs;
78 } else {
79 _port = {};
80 }
81 return *this;
82 }
83
84 [[nodiscard]] constexpr friend bool operator==(authority_type const&, authority_type const&) noexcept = default;
85 [[nodiscard]] constexpr friend auto operator<=>(authority_type const&, authority_type const&) noexcept = default;
86
87 [[nodiscard]] constexpr friend size_t to_string_size(authority_type const& rhs) noexcept
88 {
89 auto size = 0_uz;
90 if (rhs._userinfo) {
91 size += rhs._userinfo->size() + 1;
92 }
93 size += rhs._host.size();
94 if (rhs._port) {
95 size += rhs._port->size() + 1;
96 }
97 return size;
98 }
99
100 [[nodiscard]] constexpr friend std::string to_string(authority_type const& rhs) noexcept
101 {
102 auto r = std::string{};
103
104 if (rhs._userinfo) {
105 r += URI::encode<HI_SUB_DELIM, ':'>(*rhs._userinfo);
106 r += '@';
107 }
108
109 if (rhs._host.empty() or rhs._host.front() != '[') {
110 r += URI::encode<HI_SUB_DELIM>(rhs._host);
111 } else {
112 r += URI::encode<HI_SUB_DELIM, '[', ']', ':'>(rhs._host);
113 }
114
115 if (rhs._port) {
116 r += ':';
117 r += *rhs._port;
118 }
119
120 return r;
121 }
122
123 private:
124 using const_iterator = std::string_view::const_iterator;
125
126 std::optional<std::string> _userinfo = {};
127 std::string _host = {};
128 std::optional<std::string> _port = {};
129
130 [[nodiscard]] constexpr static void validate_host(std::string_view str)
131 {
132 if (str.starts_with('[') and not str.ends_with(']')) {
133 throw uri_error("The host-component starts with '[' has missing ']' at end");
134 } else if (str.ends_with(']') and str.starts_with('[')) {
135 throw uri_error("The host-component ends with ']' has missing '[' at start");
136 }
137 }
138
139 [[nodiscard]] constexpr static void validate_port(std::string_view str)
140 {
141 for (auto c : str) {
142 if (not(c >= '0' and c <= '9')) {
143 throw uri_error("The port-component contains a non-digit.");
144 }
145 }
146 }
147
155 [[nodiscard]] constexpr const_iterator parse_userinfo(const_iterator first, const_iterator last) noexcept
156 {
157 for (auto it = first; it != last; ++it) {
158 if (hilet c = *it; c == '@') {
159 set_userinfo(URI::decode(first, it));
160 return it + 1; // Skip over '@'.
161 }
162 }
163
164 // Reached at end of URI, this is not a userinfo.
165 return first;
166 }
167
174 [[nodiscard]] constexpr const_iterator parse_host(const_iterator first, const_iterator last) noexcept
175 {
176 if (first == last) {
177 return first;
178 }
179
180 auto it = first;
181 if (*it == '[') {
182 while (it != last) {
183 if (*it == ']') {
184 set_host(URI::decode(first, it + 1));
185 return it + 1; // Skip over ']'.
186 }
187 }
188 // End of URI mean this is not a host, interpret as path instead.
189 return first;
190
191 } else {
192 for (; it != last; ++it) {
193 if (hilet c = *it; c == ':') {
194 // End of host.
195 set_host(URI::decode(first, it));
196 return it;
197 }
198 }
199
200 // End of URI means that the host is the last part of the URI
201 set_host(URI::decode(first, last));
202 return last;
203 }
204 }
205
206 [[nodiscard]] constexpr const_iterator parse_port(const_iterator first, const_iterator last) noexcept
207 {
208 set_port(std::string{first, last});
209 return last;
210 }
211
212 constexpr void parse(std::string_view rhs) noexcept
213 {
214 auto first = rhs.cbegin();
215 auto last = rhs.cend();
216 auto it = parse_userinfo(first, last);
217 it = parse_host(it, last);
218
219 if (it != last and *it == ':') {
220 it = parse_port(++it, last);
221 }
222 }
223 };
224
255 class path_type : public std::vector<std::string> {
256 public:
257 constexpr path_type() noexcept = default;
258 constexpr path_type(path_type const&) noexcept = default;
259 constexpr path_type(path_type&&) noexcept = default;
260 constexpr path_type& operator=(path_type const&) noexcept = default;
261 constexpr path_type& operator=(path_type&&) noexcept = default;
262
263 [[nodiscard]] constexpr static std::vector<std::string> parse(std::string_view str)
264 {
265 auto r = make_vector<std::string>(std::views::transform(std::views::split(str, std::string_view{"/"}), [](auto&& x) {
266 return URI::decode(x);
267 }));
268
269 if (r.size() == 1 and r.front().empty()) {
270 // An empty string will evaluate to a single segment.
271 r.clear();
272 }
273 if (not r.empty() and (r.back() == "." or r.back() == ".." or r.back() == "**")) {
274 // ".", ".." and "**" are directories always terminate with a slash.
275 r.emplace_back();
276 }
277
278 return r;
279 }
280
281 constexpr path_type(std::string_view str) noexcept : std::vector<std::string>(parse(str))
282 {
283 hi_axiom(holds_invariant());
284 }
285
286 [[nodiscard]] constexpr bool absolute() const noexcept
287 {
288 return size() >= 2 and front().empty();
289 }
290
291 [[nodiscard]] constexpr bool double_absolute() const noexcept
292 {
293 return size() >= 3 and (*this)[0].empty() and (*this)[1].empty();
294 }
295
296 [[nodiscard]] constexpr bool is_root() const noexcept
297 {
298 return size() == 2 and (*this)[0].empty() and (*this)[1].empty();
299 }
300
301 [[nodiscard]] constexpr std::optional<std::string> filename() const noexcept
302 {
303 if (empty() or back().empty()) {
304 return {};
305 } else {
306 return back();
307 }
308 }
309
315 constexpr path_type& remove_filename() noexcept
316 {
317 switch (size()) {
318 case 0: // Don't remove a filename from an empty path.
319 break;
320 case 1: // relative filename, make the path empty.
321 clear();
322 break;
323 default: // relative or absolute directory with optional filename. Just empty the last path segment.
324 back().clear();
325 }
326 hi_axiom(holds_invariant());
327 return *this;
328 }
329
330 [[nodiscard]] constexpr friend path_type merge(path_type base, path_type const& ref, bool base_has_authority) noexcept
331 {
332 if (base_has_authority and base.empty()) {
333 // A URL with an authority and empty path is implicitly the root path.
334 base.emplace_back();
335 base.emplace_back();
336 }
337
338 if (not base.empty()) {
339 // Remove the (possibly empty) filename from the base.
340 base.pop_back();
341 }
342 base.insert(base.cend(), ref.cbegin(), ref.cend());
343
344 if (base.size() == 1 and base.front().empty()) {
345 // Empty ref-path added to root base-path, fix by appending an empty filename.
346 base.emplace_back();
347 }
348
349 hi_axiom(base.holds_invariant());
350 return base;
351 }
352
378 [[nodiscard]] constexpr friend path_type remove_dot_segments(path_type path) noexcept
379 {
380 for (auto it = path.cbegin(); it != path.cend();) {
381 if (*it == ".") {
382 // Remove any "." from the path.
383 it = path.erase(it);
384
385 } else if (*it == "..") {
386 if (it == path.cbegin()) {
387 // Remove the ".." at the start of a relative path.
388 it = path.erase(it);
389
390 } else if (it - 1 == path.cbegin() and (it - 1)->empty()) {
391 // Remove just the ".." at the start of an absolute path.
392 it = path.erase(it);
393
394 } else {
395 // Remove ".." and the segment in front of it.
396 it = path.erase(it - 1, it + 1);
397 }
398 } else {
399 // Ignore other segments.
400 ++it;
401 }
402 }
403
404 if (path.size() == 1 and path.front().empty()) {
405 // After removing the ".." at the start of the path we are left with an empty segment.
406 path.clear();
407 }
408
409 hi_axiom(path.holds_invariant());
410 return path;
411 }
412
413 [[nodiscard]] constexpr bool holds_invariant() const noexcept
414 {
415 if (empty()) {
416 return true;
417 }
418
419 if (back() == "." or back() == ".." or back() == "**") {
420 // ".", ".." and "**" are always directories and may not be the last segment.
421 return false;
422 }
423 return true;
424 }
425
426 [[nodiscard]] constexpr friend size_t to_string_size(path_type const& rhs) noexcept
427 {
428 size_t r = 0_uz;
429 for (hilet& segment : rhs) {
430 r += segment.size();
431 }
432 r += rhs.size() + 1;
433 return r;
434 }
435
440 [[nodiscard]] constexpr friend std::string to_string(path_type const& rhs, bool has_scheme = false) noexcept
441 {
442 auto r = std::string{};
443 r.reserve(to_string_size(rhs));
444
445 auto segment_is_empty = false;
446 for (auto it = rhs.cbegin(); it != rhs.cend(); ++it) {
447 segment_is_empty = it->empty();
448
449 if (it == rhs.cbegin() and not has_scheme) {
450 r += URI::encode<HI_SUB_DELIM, '@'>(*it);
451 } else {
452 r += URI::encode<HI_PCHAR>(*it);
453 }
454 r += '/';
455 }
456
457 if (not r.empty() and not segment_is_empty) {
458 // The last path-component was a filename, remove the trailing slash '/'.
459 r.pop_back();
460 }
461 return r;
462 }
463 };
464
465 constexpr URI() noexcept = default;
466 constexpr URI(URI const&) noexcept = default;
467 constexpr URI(URI&&) noexcept = default;
468 constexpr URI& operator=(URI const&) noexcept = default;
469 constexpr URI& operator=(URI&&) noexcept = default;
470
476 constexpr explicit URI(std::string_view str)
477 {
478 parse(str);
479 }
480
481 constexpr explicit URI(std::string const& str) : URI(std::string_view{str}) {}
482
483 constexpr explicit URI(const char *str) : URI(std::string_view{str}) {}
484
485 [[nodiscard]] constexpr bool empty() const noexcept
486 {
487 return not _scheme and not _authority and _path.empty() and not _query and not _fragment;
488 }
489
490 constexpr operator bool() const noexcept
491 {
492 return not empty();
493 }
494
499 [[nodiscard]] constexpr std::optional<std::string> const& scheme() const noexcept
500 {
501 return _scheme;
502 }
503
507 constexpr URI& set_scheme(std::optional<std::string> const& rhs)
508 {
509 if (rhs) {
510 validate_scheme(*rhs);
511 _scheme = *rhs;
512 } else {
513 _scheme = {};
514 }
515 return *this;
516 }
517
522 [[nodiscard]] constexpr std::optional<authority_type> const& authority() const noexcept
523 {
524 return _authority;
525 }
526
527 constexpr URI& set_authority(std::optional<authority_type> const& rhs) noexcept
528 {
529 _authority = rhs;
530 return *this;
531 }
532
533 [[nodiscard]] constexpr path_type const& path() const noexcept
534 {
535 return _path;
536 }
537
538 constexpr URI& set_path(path_type const& rhs)
539 {
540 validate_path(rhs, to_bool(_authority));
541 _path = rhs;
542 return *this;
543 }
544
545 [[nodiscard]] constexpr std::optional<std::string> filename() const noexcept
546 {
547 return _path.filename();
548 }
549
555 constexpr URI& remove_filename() noexcept
556 {
557 _path.remove_filename();
558 return *this;
559 }
560
565 [[nodiscard]] constexpr std::optional<std::string> const& query() const noexcept
566 {
567 return _query;
568 }
569
570 constexpr URI& set_query(std::optional<std::string> const& rhs) noexcept
571 {
572 _query = rhs;
573 return *this;
574 }
575
580 [[nodiscard]] constexpr std::optional<std::string> const& fragment() const noexcept
581 {
582 return _fragment;
583 }
584
585 constexpr URI& set_fragment(std::optional<std::string> const& rhs) noexcept
586 {
587 _fragment = rhs;
588 return *this;
589 }
590
591 [[nodiscard]] constexpr friend std::string to_string(URI const& rhs) noexcept
592 {
593 auto r = std::string{};
594 r.reserve(to_string_size(rhs));
595
596 if (rhs._scheme) {
597 r += *rhs._scheme;
598 r += ':';
599 }
600
601 if (rhs._authority) {
602 r += '/';
603 r += '/';
604 r += to_string(*rhs._authority);
605 }
606
607 r += to_string(rhs._path, to_bool(rhs._scheme));
608
609 if (rhs._query) {
610 r += '?';
611 r += URI::encode<HI_PCHAR, '/', '?'>(*rhs._query);
612 }
613
614 if (rhs._fragment) {
615 r += '#';
616 r += URI::encode<HI_PCHAR, '/', '?'>(*rhs._fragment);
617 }
618
619 return r;
620 }
621
622 friend std::ostream& operator<<(std::ostream& lhs, URI const& rhs) noexcept
623 {
624 return lhs << to_string(rhs);
625 }
626
627 [[nodiscard]] constexpr friend bool operator==(URI const& lhs, URI const& rhs) noexcept = default;
628 [[nodiscard]] constexpr friend auto operator<=>(URI const& lhs, URI const& rhs) noexcept = default;
629
630 [[nodiscard]] constexpr friend URI operator/(URI const& base, URI const& ref) noexcept
631 {
632 auto target = URI{};
633
634 if (ref._scheme) {
635 target._scheme = ref._scheme;
636 target._authority = ref._authority;
637 target._path = remove_dot_segments(ref._path);
638 target._query = ref._query;
639 } else {
640 if (ref._authority) {
641 target._authority = ref._authority;
642 target._path = remove_dot_segments(ref._path);
643 target._query = ref._query;
644
645 } else {
646 if (ref._path.empty()) {
647 target._path = base._path;
648 if (ref._query) {
649 target._query = ref._query;
650 } else {
651 target._query = base._query;
652 }
653 } else {
654 if (ref._path.absolute()) {
655 target._path = remove_dot_segments(ref._path);
656 } else {
657 target._path = remove_dot_segments(merge(base._path, ref._path, to_bool(base._authority)));
658 }
659 target._query = ref._query;
660 }
661 target._authority = base._authority;
662 }
663 target._scheme = base._scheme;
664 }
665
666 target._fragment = ref._fragment;
667
668 return target;
669 }
670
671 [[nodiscard]] constexpr friend bool operator==(URI const& lhs, std::string_view rhs) noexcept
672 {
673 return lhs == URI(rhs);
674 }
675
676 [[nodiscard]] constexpr friend auto operator<=>(URI const& lhs, std::string_view rhs) noexcept
677 {
678 return lhs == URI(rhs);
679 }
680
681 [[nodiscard]] constexpr friend URI operator/(URI const& base, std::string_view ref) noexcept
682 {
683 return base / URI(ref);
684 }
685
691 [[nodiscard]] constexpr static std::string
692 decode(auto first, auto last) requires std::is_same_v < std::decay_t<decltype(*first)>,
693 char >
694 {
695 auto r = std::string{};
696 if constexpr (requires { std::distance(first, last); }) {
697 r.reserve(std::distance(first, last));
698 }
699
700 auto state = 2;
701 uint8_t code_unit = 0;
702 for (auto it = first; it != last; ++it) {
703 hilet c = *it;
704 switch (state) {
705 case 0:
706 [[fallthrough]];
707 case 1:
708 code_unit <<= 4;
709 if (c >= '0' and c <= '9') {
710 code_unit |= c & 0xf;
711 } else if ((c >= 'a' and c <= 'f') or (c >= 'A' and c <= 'F')) {
712 code_unit |= (c & 0xf) + 9;
713 } else {
714 throw uri_error("Unexpected character in percent-encoding.");
715 }
716 if (++state == 2) {
717 r += char_cast<char>(code_unit);
718 }
719 break;
720 case 2:
721 if (c == '%') {
722 state = 0;
723 } else {
724 r += c;
725 }
726 break;
727 default:
728 hi_no_default();
729 }
730 }
731
732 if (state != 2) {
733 throw uri_error("Unexpected end of URI component inside percent-encoding.");
734 }
735
736 return r;
737 }
738
744 [[nodiscard]] constexpr static std::string decode(auto&& range)
745 {
746 return decode(std::ranges::begin(range), std::ranges::end(range));
747 }
748
755 template<char... Extras, typename It, typename ItEnd>
756 [[nodiscard]] constexpr static std::string encode(It first, ItEnd last) noexcept
757 {
758 auto r = std::string{};
759 if constexpr (requires { std::distance(first, last); }) {
760 r.reserve(std::distance(first, last));
761 }
762
763 for (auto it = first; it != last; ++it) {
764 hilet c = *it;
765 // clang-format off
766 if (((
767 (c >= 'a' and c <= 'z') or
768 (c >= 'A' and c <= 'Z') or
769 (c >= '0' and c <= '9') or
770 c == '-' or c == '.' or c == '_' or c == '~'
771 ) or ... or (c == Extras))) {
772 r += c;
773
774 } else {
775 r += '%';
776 auto uc = char_cast<uint8_t>(c);
777 if (auto nibble = narrow_cast<char>(uc >> 4); nibble <= 9) {
778 r += '0' + nibble;
779 } else {
780 r += 'A' + nibble - 10;
781 }
782 if (auto nibble = narrow_cast<char>(uc & 0xf); nibble <= 9) {
783 r += '0' + nibble;
784 } else {
785 r += 'A' + nibble - 10;
786 }
787 }
788 // clang-format on
789 }
790
791 return r;
792 }
793
800 template<char... Extras, typename Range>
801 [[nodiscard]] constexpr static std::string encode(Range&& range) noexcept
802 {
803 return encode<Extras...>(std::ranges::begin(range), std::ranges::end(range));
804 }
805
806private:
807 using const_iterator = std::string_view::const_iterator;
808
809 std::optional<std::string> _scheme;
810 std::optional<authority_type> _authority;
811 path_type _path;
812 std::optional<std::string> _query;
813 std::optional<std::string> _fragment;
814
815 [[nodiscard]] constexpr friend size_t to_string_size(URI const& rhs) noexcept
816 {
817 auto size = 0_uz;
818 if (rhs._scheme) {
819 size += rhs._scheme->size() + 1;
820 }
821 if (rhs._authority) {
822 size += to_string_size(*rhs._authority) + 2;
823 }
824 size += to_string_size(rhs._path);
825
826 if (rhs._query) {
827 size += rhs._query->size() + 1;
828 }
829 if (rhs._fragment) {
830 size += rhs._fragment->size() + 1;
831 }
832
833 return size;
834 }
835
836 [[nodiscard]] constexpr static bool is_scheme_start(char c) noexcept
837 {
838 return (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z');
839 }
840
841 [[nodiscard]] constexpr static bool is_scheme(char c) noexcept
842 {
843 return is_scheme_start(c) or (c >= '0' and c <= '9') or c == '+' or c == '-' or c == '.';
844 }
845
846 [[nodiscard]] constexpr static void validate_scheme(std::string_view str)
847 {
848 if (str.empty()) {
849 throw uri_error("The scheme-component is not allowed to be empty (it is allowed to not exist).");
850 }
851 if (not is_scheme_start(str.front())) {
852 throw uri_error("The scheme-component does not start with [a-zA-Z].");
853 }
854 for (auto c : str) {
855 if (not is_scheme(c)) {
856 throw uri_error("The scheme-component contains a character outside [a-zA-Z0-9.+-].");
857 }
858 }
859 }
860
861 [[nodiscard]] constexpr static void validate_path(path_type const& path, bool has_authority)
862 {
863 if (has_authority) {
864 if (not(path.empty() or path.absolute())) {
865 throw uri_error("A path-component in a URI with an authority-component must be empty or absolute.");
866 }
867 } else if (path.double_absolute()) {
868 throw uri_error("A path-component in a URI without an authority-component may not start with a double slash '//'.");
869 }
870 }
871
878 [[nodiscard]] constexpr const_iterator parse_scheme(const_iterator first, const_iterator last)
879 {
880 for (auto it = first; it != last; ++it) {
881 if (hilet c = *it; c == ':') {
882 set_scheme(std::string{first, it});
883 return it + 1; // Skip over ':'.
884
885 } else if (c == '/' or c == '?' or c == '#') {
886 // Invalid character, this is not a scheme.
887 return first;
888 }
889 }
890
891 // Reached at end of URI, this is not a scheme.
892 return first;
893 }
894
895 [[nodiscard]] constexpr const_iterator parse_authority(const_iterator first, const_iterator last)
896 {
897 for (auto it = first; it != last; it++) {
898 if (hilet c = *it; c == '/' or c == '?' or c == '#') {
899 set_authority(authority_type{std::string_view{first, it}});
900 return it;
901 }
902 }
903
904 set_authority(authority_type{std::string_view{first, last}});
905 return last;
906 }
907
908 [[nodiscard]] constexpr const_iterator parse_path(const_iterator first, const_iterator last)
909 {
910 for (auto it = first; it != last; it++) {
911 if (hilet c = *it; c == '?' or c == '#') {
912 set_path(path_type{std::string_view{first, it}});
913 return it;
914 }
915 }
916
917 set_path(path_type{std::string_view{first, last}});
918 return last;
919 }
920
921 [[nodiscard]] constexpr const_iterator parse_query(const_iterator first, const_iterator last)
922 {
923 for (auto it = first; it != last; it++) {
924 if (hilet c = *it; c == '#') {
925 set_query(URI::decode(first, it));
926 return it;
927 }
928 }
929
930 set_query(URI::decode(first, last));
931 return last;
932 }
933
934 [[nodiscard]] constexpr const_iterator parse_fragment(const_iterator first, const_iterator last)
935 {
936 set_fragment(URI::decode(first, last));
937 return last;
938 }
939
940 constexpr void parse(std::string_view str)
941 {
942 auto first = str.cbegin();
943 auto last = str.cend();
944 auto it = parse_scheme(first, last);
945
946 if (std::distance(it, last) >= 2 and it[0] == '/' and it[1] == '/') {
947 it += 2;
948 it = parse_authority(it, last);
949 }
950
951 it = parse_path(it, last);
952
953 if (it != last and *it == '?') {
954 it = parse_query(++it, last);
955 }
956
957 if (it != last and *it == '#') {
958 it = parse_fragment(++it, last);
959 }
960 }
961
962 friend struct std::hash<URI>;
963};
964
965#undef HI_PCHAR
966#undef HI_SUB_DELIM
967
968}} // namespace hi::v1
969
970template<>
971struct std::hash<hi::URI> {
972 [[nodiscard]] size_t operator()(hi::URI const& rhs) const noexcept
973 {
974 return std::hash<std::string>{}(to_string(rhs));
975 }
976};
977
978template<typename CharT>
979struct std::formatter<hi::URI, CharT> : std::formatter<std::string, CharT> {
980 auto format(hi::URI const& t, auto& fc)
981 {
982 return std::formatter<std::string, CharT>::format(to_string(t), fc);
983 }
984};
#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
Definition URI.hpp:25
static constexpr std::string decode(auto first, auto last)
URI percent-encoding decode function.
Definition URI.hpp:692
static constexpr std::string decode(auto &&range)
URI percent-encoding decode function.
Definition URI.hpp:744
constexpr std::optional< std::string > const & query() const noexcept
Get the query-component of the URI.
Definition URI.hpp:565
constexpr std::optional< std::string > const & fragment() const noexcept
Get the fragment-component of the URI.
Definition URI.hpp:580
static constexpr std::string encode(Range &&range) noexcept
URI encode a component.
Definition URI.hpp:801
static constexpr std::string encode(It first, ItEnd last) noexcept
URI encode a component.
Definition URI.hpp:756
constexpr std::optional< authority_type > const & authority() const noexcept
Get the authority-component of the URI.
Definition URI.hpp:522
constexpr URI & remove_filename() noexcept
Remove the filename part of the path.
Definition URI.hpp:555
constexpr std::optional< std::string > const & scheme() const noexcept
Get the scheme-component of the URI.
Definition URI.hpp:499
constexpr URI & set_scheme(std::optional< std::string > const &rhs)
Get the scheme-component of the URI.
Definition URI.hpp:507
Definition URI.hpp:27
A path type.
Definition URI.hpp:255
constexpr path_type & remove_filename() noexcept
Remove the filename part of the path.
Definition URI.hpp:315
constexpr friend std::string to_string(path_type const &rhs, bool has_scheme=false) noexcept
Definition URI.hpp:440
constexpr friend path_type remove_dot_segments(path_type path) noexcept
Definition URI.hpp:378
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)