HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
URL.hpp
Go to the documentation of this file.
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/module.hpp"
15#include <string>
16#include <string_view>
17#include <optional>
18#include <vector>
19#include <unordered_map>
20#include <memory>
21#include <ostream>
22#include <mutex>
23#include <filesystem>
24
25hi_warning_push();
26// C26434: Function '' hides a non-virtual function ''.
27// False positive reported: https://developercommunity.visualstudio.com/t/C26434-false-positive-with-conversion-op/10262199
28hi_warning_ignore_msvc(26434);
29
30
31namespace hi { inline namespace v1 {
32
49class URL : public URI {
50public:
53 constexpr URL() noexcept = default;
54 constexpr URL(URL const&) noexcept = default;
55 constexpr URL(URL&&) noexcept = default;
56 constexpr URL& operator=(URL const&) noexcept = default;
57 constexpr URL& operator=(URL&&) noexcept = default;
58
61 constexpr explicit URL(URI const& other) noexcept : URI(other) {}
62
65 constexpr explicit URL(URI&& other) noexcept : URI(std::move(other)){};
66
74 explicit URL(std::filesystem::path const& path) : URI(make_file_url_string(path)) {}
75
81 [[nodiscard]] constexpr std::u8string filesystem_path_generic_u8string(bool validate_scheme = true) const
82 {
83 if (validate_scheme and not(not scheme() or scheme() == "file")) {
84 throw url_error("URL::generic_path() is only valid on a file: scheme URL");
85 }
86
87 auto r = std::string{};
88 hilet& p = path();
89 hilet first = p.begin();
90 hilet last = p.end();
91 auto it = first;
92
93 auto has_root_name = false;
94 if (authority()) {
95 // file://server/filename is valid.
96 auto server = to_string(*authority());
97 if (not server.empty() and server != "localhost") {
98 validate_file_server(server);
99 has_root_name = true;
100 r += '/';
101 r += '/';
102 r += server;
103 r += '/';
104 }
105 }
106
107 // If a server was found than the path must be absolute.
108 hi_assert(has_root_name == false or p.absolute());
109
110 if (p.double_absolute()) {
111 // file:////server/filename is valid.
112 if (has_root_name) {
113 // file://server//server/filename is invalid.
114 throw url_error("file URL has two server names.");
115 }
116
117 has_root_name = true;
118 r += '/';
119 r += '/';
120 it += 2;
121 validate_file_server(*it);
122 r += *it++;
123 r += '/';
124 }
125
126 // Find the drive letter.
127 auto empty_segment = false;
128 while (it != last) {
129 validate_file_segment(*it);
130 empty_segment = it->empty();
131
132 if (it == first and empty_segment) {
133 // Skip leading '/' in front of drive letter.
134 ++it;
135
136 } else if (auto i = it->find(':'); i != std::string::npos) {
137 // Found a drive letter.
138 if (i != 1) {
139 throw url_error("file URL contains a device name which is a security issue.");
140 }
141
142 if (has_root_name or p.absolute()) {
143 r += it->front();
144 // Use $ when the drive letter is on a server.
145 r += has_root_name ? '$' : ':';
146 // Add potentially a relative segment after the driver letter.
147 r += it->substr(2);
148
149 } else {
150 // Take the drive letter and optional relative directory directly on relative paths.
151 // C:dirname valid
152 // C:/dirname valid
153 // file:C:dirname valid
154 // file:C:/dirname valid
155 r += *it;
156 }
157
158 has_root_name = true;
159 r += '/';
160 ++it;
161 break;
162
163 } else {
164 // Found a normal directory or filename.
165 break;
166 }
167 }
168
169 // The rest are directories followed by a single (optionally empty) filename
170 for (; it != last; ++it) {
171 validate_file_segment(*it);
172 empty_segment = it->empty();
173 r += *it;
174 r += '/';
175 }
176
177 if (not empty_segment) {
178 // Remove trailing backslash if the last segment was a filename.
179 r.pop_back();
180 }
181
182 return to_u8string(r);
183 }
184
190 [[nodiscard]] std::filesystem::path filesystem_path() const
191 {
192 if (auto scheme_ = scheme()) {
193 if (scheme_ == "resource") {
194 // Always used std::u8string with std::filesystem::path.
195 hilet ref = std::filesystem::path{filesystem_path_generic_u8string(false)};
197 return *path;
198 } else {
199 throw url_error(std::format("Resource {} not found.", *this));
200 }
201
202 } else if (scheme_ == "file") {
204 } else {
205 throw url_error("URL can not be converted to a filesystem path.");
206 }
207 } else {
208 // relative path.
210 }
211 }
212
215 operator std::filesystem::path() const
216 {
217 return filesystem_path();
218 }
219
220 [[nodiscard]] constexpr friend URL operator/(URL const& base, URI const& ref) noexcept
221 {
222 return URL{up_cast<URI const&>(base) / ref};
223 }
224
225 [[nodiscard]] constexpr friend URL operator/(URL const& base, std::string_view ref) noexcept
226 {
227 return URL{up_cast<URI const&>(base) / ref};
228 }
229
230private:
231 constexpr void static validate_file_segment(std::string_view segment)
232 {
233 for (auto c : segment) {
234 if (c == '/' or c == '\\') {
235 throw url_error("Filename server name may not contain slash or back-slash.");
236 }
237 }
238 }
239
240 constexpr void static validate_file_server(std::string_view server)
241 {
242 for (auto c : server) {
243 if (c == '/' or c == '\\') {
244 throw url_error("Filename segments may not contain slash or back-slash.");
245 }
246 }
247 }
248
249 static std::string make_file_url_string(std::filesystem::path const& path)
250 {
251 auto r = std::u8string{};
252
253 hilet root_name = path.root_name().generic_u8string();
254 if (root_name.empty()) {
255 // No root-name.
256 if (not path.root_directory().empty()) {
257 // An absolute path should start with the file: scheme.
258 r += u8"file:" + path.root_directory().generic_u8string();
259 } else {
260 // A relative path should not be prefixed with a scheme.
261 ;
262 }
263
264 } else if (hilet i = root_name.find(':'); i != std::string::npos) {
265 if (i == 1) {
266 // Root name is a drive-letter, followed by potentially a relative path.
267 r += u8"file:///" + root_name + path.root_directory().generic_u8string();
268 } else {
269 throw url_error("Paths containing a device are not allowed to be converted to a URL.");
270 }
271 } else {
272 // Root name is a server.
273 r += u8"file://" + root_name + path.root_directory().generic_u8string();
274 if (not path.root_directory().empty()) {
275 throw url_error("Invalid path contains server name without a root directory.");
276 }
277 }
278
279 return to_string(r + path.relative_path().generic_u8string());
280 }
281};
282
283}} // namespace hi::v1
284
285template<>
286struct std::hash<hi::URL> {
287 [[nodiscard]] size_t operator()(hi::URL const& rhs) const noexcept
288 {
289 return std::hash<hi::URI>{}(rhs);
290 }
291};
292
293template<typename CharT>
294struct std::formatter<hi::URL, CharT> : std::formatter<hi::URI, CharT> {
295 auto format(hi::URL const& t, auto& fc)
296 {
297 return std::formatter<hi::URI, CharT>::format(t, fc);
298 }
299};
300
301hi_warning_pop();
functions to locate files and directories.
Defines the URI class.
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:184
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
constexpr std::u8string to_u8string(std::u32string_view rhs) noexcept
Conversion from UTF-32 to UTF-8.
Definition to_string.hpp:111
std::optional< std::filesystem::path > find_path(path_location location, std::filesystem::path const &ref) noexcept
Find a path.
Definition path_location.hpp:83
@ resource_dirs
The location of application resources.
@ other
The gui_event does not have associated data.
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11
A Uniform Resource Identifier.
Definition URI.hpp:37
constexpr std::optional< authority_type > const & authority() const noexcept
Get the authority-component of the URI.
Definition URI.hpp:537
constexpr std::optional< std::string > const & scheme() const noexcept
Get the scheme-component of the URI.
Definition URI.hpp:514
Universal Resource Locator.
Definition URL.hpp:49
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:74
constexpr URL(URI &&other) noexcept
Convert a URI to an URL.
Definition URL.hpp:65
operator std::filesystem::path() const
Definition URL.hpp:215
std::filesystem::path filesystem_path() const
Create a filesystem path from a file URL.
Definition URL.hpp:190
constexpr std::u8string filesystem_path_generic_u8string(bool validate_scheme=true) const
Return a generic path.
Definition URL.hpp:81
Definition exception.hpp:200
T move(T... args)
T operator()(T... args)
T to_string(T... args)