HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
URL.hpp
1// Copyright Take Vos 2019-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 "URI.hpp"
12#include "path_location.hpp"
13#include "../char_maps/module.hpp"
14#include "../utility/utility.hpp"
15#include "../macros.hpp"
16#include <string>
17#include <string_view>
18#include <optional>
19#include <vector>
20#include <unordered_map>
21#include <memory>
22#include <ostream>
23#include <mutex>
24#include <filesystem>
25
26hi_export_module(hikogui.path.URL);
27
28hi_warning_push();
29// C26434: Function '' hides a non-virtual function ''.
30// False positive reported: https://developercommunity.visualstudio.com/t/C26434-false-positive-with-conversion-op/10262199
31hi_warning_ignore_msvc(26434);
32
33namespace hi { inline namespace v1 {
34
51class URL : public URI {
52public:
55 constexpr URL() noexcept = default;
60
64
67 constexpr explicit URL(URI&& other) noexcept : URI(std::move(other)){};
68
75 constexpr explicit URL(std::string_view str) : URI(str) {}
76
83 constexpr explicit URL(std::string const& str) : URL(std::string_view{str}) {}
84
91 constexpr explicit URL(const char *str) : URL(std::string_view{str}) {}
92
100 explicit URL(std::filesystem::path const& path) : URI(make_file_url_string(path)) {}
101
107 [[nodiscard]] constexpr std::u8string filesystem_path_generic_u8string(bool validate_scheme = true) const
108 {
109 if (validate_scheme and not(not scheme() or scheme() == "file")) {
110 throw url_error("URL::generic_path() is only valid on a file: scheme URL");
111 }
112
113 auto r = std::string{};
114 hilet& p = path();
115 hilet first = p.begin();
116 hilet last = p.end();
117 auto it = first;
118
119 auto has_root_name = false;
120 if (authority()) {
121 // file://server/filename is valid.
122 auto server = to_string(*authority());
123 if (not server.empty() and server != "localhost") {
124 validate_file_server(server);
125 has_root_name = true;
126 r += '/';
127 r += '/';
128 r += server;
129 r += '/';
130 }
131 }
132
133 // If a server was found than the path must be absolute.
134 hi_assert(has_root_name == false or p.absolute());
135
136 if (p.double_absolute()) {
137 // file:////server/filename is valid.
138 if (has_root_name) {
139 // file://server//server/filename is invalid.
140 throw url_error("file URL has two server names.");
141 }
142
143 has_root_name = true;
144 r += '/';
145 r += '/';
146 it += 2;
147 validate_file_server(*it);
148 r += *it++;
149 r += '/';
150 }
151
152 // Find the drive letter.
153 auto empty_segment = false;
154 while (it != last) {
155 validate_file_segment(*it);
156 empty_segment = it->empty();
157
158 if (it == first and empty_segment) {
159 // Skip leading '/' in front of drive letter.
160 ++it;
161
162 } else if (auto i = it->find(':'); i != std::string::npos) {
163 // Found a drive letter.
164 if (i != 1) {
165 throw url_error("file URL contains a device name which is a security issue.");
166 }
167
168 if (has_root_name or p.absolute()) {
169 r += it->front();
170 // Use $ when the drive letter is on a server.
171 r += has_root_name ? '$' : ':';
172 // Add potentially a relative segment after the driver letter.
173 r += it->substr(2);
174
175 } else {
176 // Take the drive letter and optional relative directory directly on relative paths.
177 // C:dirname valid
178 // C:/dirname valid
179 // file:C:dirname valid
180 // file:C:/dirname valid
181 r += *it;
182 }
183
184 has_root_name = true;
185 r += '/';
186 ++it;
187 break;
188
189 } else {
190 // Found a normal directory or filename.
191 break;
192 }
193 }
194
195 // The rest are directories followed by a single (optionally empty) filename
196 for (; it != last; ++it) {
197 validate_file_segment(*it);
198 empty_segment = it->empty();
199 r += *it;
200 r += '/';
201 }
202
203 if (not empty_segment) {
204 // Remove trailing backslash if the last segment was a filename.
205 r.pop_back();
206 }
207
208 return to_u8string(r);
209 }
210
216 [[nodiscard]] std::filesystem::path filesystem_path() const
217 {
218 if (auto scheme_ = scheme()) {
219 if (scheme_ == "resource") {
220 // Always used std::u8string with std::filesystem::path.
221 hilet ref = std::filesystem::path{filesystem_path_generic_u8string(false)};
223 return *path;
224 } else {
225 throw url_error(std::format("Resource {} not found.", *this));
226 }
227
228 } else if (scheme_ == "file") {
230 } else {
231 throw url_error("URL can not be converted to a filesystem path.");
232 }
233 } else {
234 // relative path.
236 }
237 }
238
241 operator std::filesystem::path() const
242 {
243 return filesystem_path();
244 }
245
246 [[nodiscard]] constexpr friend URL operator/(URL const& base, URI const& ref) noexcept
247 {
248 return URL{up_cast<URI const&>(base) / ref};
249 }
250
251 [[nodiscard]] constexpr friend URL operator/(URL const& base, std::string_view ref) noexcept
252 {
253 return URL{up_cast<URI const&>(base) / ref};
254 }
255
256private:
257 constexpr void static validate_file_segment(std::string_view segment)
258 {
259 for (auto c : segment) {
260 if (c == '/' or c == '\\') {
261 throw url_error("Filename server name may not contain slash or back-slash.");
262 }
263 }
264 }
265
266 constexpr void static validate_file_server(std::string_view server)
267 {
268 for (auto c : server) {
269 if (c == '/' or c == '\\') {
270 throw url_error("Filename segments may not contain slash or back-slash.");
271 }
272 }
273 }
274
275 static std::string make_file_url_string(std::filesystem::path const& path)
276 {
277 auto r = std::u8string{};
278
279 hilet root_name = path.root_name().generic_u8string();
280 if (root_name.empty()) {
281 // No root-name.
282 if (not path.root_directory().empty()) {
283 // An absolute path should start with the file: scheme.
284 r += u8"file:" + path.root_directory().generic_u8string();
285 } else {
286 // A relative path should not be prefixed with a scheme.
287 ;
288 }
289
290 } else if (hilet i = root_name.find(':'); i != std::string::npos) {
291 if (i == 1) {
292 // Root name is a drive-letter, followed by potentially a relative path.
293 r += u8"file:///" + root_name + path.root_directory().generic_u8string();
294 } else {
295 throw url_error("Paths containing a device are not allowed to be converted to a URL.");
296 }
297 } else {
298 // Root name is a server.
299 r += u8"file://" + root_name + path.root_directory().generic_u8string();
300 if (not path.root_directory().empty()) {
301 throw url_error("Invalid path contains server name without a root directory.");
302 }
303 }
304
305 return to_string(r + path.relative_path().generic_u8string());
306 }
307};
308
309}} // namespace hi::v1
310
311template<>
312struct std::hash<hi::URL> {
313 [[nodiscard]] size_t operator()(hi::URL const& rhs) const noexcept
314 {
315 return std::hash<hi::URI>{}(rhs);
316 }
317};
318
319template<typename CharT>
320struct std::formatter<hi::URL, CharT> : std::formatter<hi::URI, CharT> {
321 auto format(hi::URL const& t, auto& fc) const
322 {
323 return std::formatter<hi::URI, CharT>::format(t, fc);
324 }
325};
326
327hi_warning_pop();
functions to locate files and directories.
constexpr std::u8string to_u8string(std::u32string_view rhs) noexcept
Conversion from UTF-32 to UTF-8.
Definition to_string.hpp:112
std::optional< std::filesystem::path > find_path(path_location location, std::filesystem::path const &ref) noexcept
Find a path.
Definition path_location_intf.hpp:86
@ resource_dirs
The location of application resources.
@ other
The gui_event does not have associated data.
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:16
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
A Uniform Resource Identifier.
Definition URI.hpp:39
constexpr std::optional< authority_type > const & authority() const noexcept
Get the authority-component of the URI.
Definition URI.hpp:552
constexpr std::optional< std::string > const & scheme() const noexcept
Get the scheme-component of the URI.
Definition URI.hpp:529
Universal Resource Locator.
Definition URL.hpp:51
constexpr URL() noexcept=default
Create an empty URL.
URL(std::filesystem::path const &path)
Convert a filesystem-path to a file-scheme URL.
Definition URL.hpp:100
constexpr URL(URI &&other) noexcept
Convert a URI to an URL.
Definition URL.hpp:67
operator std::filesystem::path() const
Definition URL.hpp:241
constexpr URL(const char *str)
Construct a URI from a string.
Definition URL.hpp:91
constexpr URL(std::string const &str)
Construct a URI from a string.
Definition URL.hpp:83
std::filesystem::path filesystem_path() const
Create a filesystem path from a file URL.
Definition URL.hpp:216
constexpr std::u8string filesystem_path_generic_u8string(bool validate_scheme=true) const
Return a generic path.
Definition URL.hpp:107
constexpr URL(std::string_view str)
Construct a URI from a string.
Definition URL.hpp:75
Definition exception_intf.hpp:203
T move(T... args)
T operator()(T... args)
T to_string(T... args)