VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
uri.h
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3#pragma once
4
5#include <cctype>
6#include <iomanip>
7#include <map>
8#include <optional>
9#include <regex>
10#include <sstream>
11#include <stdexcept>
12#include <string>
13
14namespace visutwin::canvas
15{
16 inline std::string encodeURIComponent(const std::string& input)
17 {
18 std::ostringstream escaped;
19 escaped.fill('0');
20 escaped << std::hex;
21
22 for (const unsigned char c : input) {
23 if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
24 escaped << c;
25 } else {
26 escaped << '%' << std::uppercase << std::setw(2) << static_cast<int>(c) << std::nouppercase;
27 }
28 }
29
30 return escaped.str();
31 }
32
33 inline std::string decodeURIComponent(const std::string& input)
34 {
35 std::string decoded;
36 decoded.reserve(input.size());
37
38 for (size_t i = 0; i < input.size(); ++i) {
39 if (input[i] == '%' && i + 2 < input.size()) {
40 const auto hex = input.substr(i + 1, 2);
41 const char ch = static_cast<char>(std::stoi(hex, nullptr, 16));
42 decoded += ch;
43 i += 2;
44 } else if (input[i] == '+') {
45 decoded += ' ';
46 } else {
47 decoded += input[i];
48 }
49 }
50
51 return decoded;
52 }
53
55 {
56 std::optional<std::string> scheme;
57 std::optional<std::string> authority;
58 std::optional<std::string> host;
59 std::optional<std::string> path;
60 std::optional<std::string> hostpath;
61 std::optional<std::string> query;
62 std::optional<std::string> fragment;
63 };
64
65 inline std::string createURI(const URIOptions& options)
66 {
67 if ((options.authority || options.scheme) && (options.host || options.hostpath)) {
68 throw std::invalid_argument("Can't have 'scheme' or 'authority' and 'host' or 'hostpath' option");
69 }
70
71 if (options.host && options.hostpath) {
72 throw std::invalid_argument("Can't have 'host' and 'hostpath' option");
73 }
74
75 if (options.path && options.hostpath) {
76 throw std::invalid_argument("Can't have 'path' and 'hostpath' option");
77 }
78
79 std::string result;
80 if (options.scheme) {
81 result += *options.scheme + ":";
82 }
83 if (options.authority) {
84 result += "//" + *options.authority;
85 }
86 if (options.host) {
87 result += *options.host;
88 }
89 if (options.path) {
90 result += *options.path;
91 }
92 if (options.hostpath) {
93 result += *options.hostpath;
94 }
95 if (options.query) {
96 result += "?" + *options.query;
97 }
98 if (options.fragment) {
99 result += "#" + *options.fragment;
100 }
101
102 return result;
103 }
104
105 class URI
106 {
107 public:
108 explicit URI(const std::string& uri)
109 {
110 static const std::regex regex(R"(^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)");
111 std::smatch match;
112 std::regex_match(uri, match, regex);
113 scheme = match[2].str();
114 authority = match[4].str();
115 path = match[5].str();
116 query = match[7].str();
117 fragment = match[9].str();
118 }
119
120 [[nodiscard]] std::string toString() const
121 {
122 std::string result;
123 if (!scheme.empty()) {
124 result += scheme + ":";
125 }
126 if (!authority.empty()) {
127 result += "//" + authority;
128 }
129 result += path;
130 if (!query.empty()) {
131 result += "?" + query;
132 }
133 if (!fragment.empty()) {
134 result += "#" + fragment;
135 }
136 return result;
137 }
138
139 [[nodiscard]] std::map<std::string, std::string> getQuery() const
140 {
141 std::map<std::string, std::string> result;
142 if (query.empty()) {
143 return result;
144 }
145
146 std::stringstream stream(decodeURIComponent(query));
147 std::string kv;
148 while (std::getline(stream, kv, '&')) {
149 const size_t sep = kv.find('=');
150 if (sep == std::string::npos) {
151 result[kv] = "";
152 } else {
153 result[kv.substr(0, sep)] = kv.substr(sep + 1);
154 }
155 }
156
157 return result;
158 }
159
160 void setQuery(const std::map<std::string, std::string>& params)
161 {
162 std::string q;
163 for (const auto& [key, value] : params) {
164 if (!q.empty()) {
165 q += '&';
166 }
167 q += encodeURIComponent(key) + "=" + encodeURIComponent(value);
168 }
169 query = q;
170 }
171
172 std::string scheme;
173 std::string authority;
174 std::string path;
175 std::string query;
176 std::string fragment;
177 };
178}
std::string toString() const
Definition uri.h:120
std::string authority
Definition uri.h:173
std::map< std::string, std::string > getQuery() const
Definition uri.h:139
URI(const std::string &uri)
Definition uri.h:108
std::string query
Definition uri.h:175
std::string path
Definition uri.h:174
void setQuery(const std::map< std::string, std::string > &params)
Definition uri.h:160
std::string fragment
Definition uri.h:176
std::string scheme
Definition uri.h:172
std::string createURI(const URIOptions &options)
Definition uri.h:65
std::string decodeURIComponent(const std::string &input)
Definition uri.h:33
std::string encodeURIComponent(const std::string &input)
Definition uri.h:16
std::optional< std::string > hostpath
Definition uri.h:60
std::optional< std::string > authority
Definition uri.h:57
std::optional< std::string > scheme
Definition uri.h:56
std::optional< std::string > path
Definition uri.h:59
std::optional< std::string > query
Definition uri.h:61
std::optional< std::string > host
Definition uri.h:58
std::optional< std::string > fragment
Definition uri.h:62