载入中...
搜索中...
未找到
Uri.cpp
浏览该文件的文档.
1#include "common/Uri.h"
2
3#include <algorithm>
4#include <cctype>
5#include <optional>
6#include <utility>
7
8namespace eve {
9namespace {
10
11[[nodiscard]] Result<Uri> uriFailure(std::string message, std::string_view path = {}) {
13}
14
15[[nodiscard]] Result<ResourceUri> resourceUriFailure(std::string message, std::string_view path = {}) {
18}
19
20[[nodiscard]] bool isSchemeCharacter(char value, bool first) noexcept {
21 const bool alpha = (value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z');
22 const bool digit = value >= '0' && value <= '9';
23 return first ? alpha : (alpha || digit || value == '+' || value == '-' || value == '.');
24}
25
26[[nodiscard]] bool isPathCharacter(char value) noexcept {
27 const auto byte = static_cast<unsigned char>(value);
28 if (byte < 0x21 || byte > 0x7e) return false;
29 switch (value) {
30 case '/':
31 case '_':
32 case '-':
33 case '.':
34 case '~':
35 case '+':
36 case '@': return true;
37 default: return std::isalnum(byte) != 0;
38 }
39}
40
41[[nodiscard]] bool isQueryOrFragmentCharacter(char value) noexcept {
42 const auto byte = static_cast<unsigned char>(value);
43 if (byte < 0x21 || byte > 0x7e) return false;
44 return value != '?' && value != '#' && value != '%' && value != '\\';
45}
46
47[[nodiscard]] std::optional<UriScheme> schemeFromText(std::string_view scheme) noexcept {
48 if (scheme == "asset") return UriScheme::Asset;
49 if (scheme == "project") return UriScheme::Project;
50 if (scheme == "builtin") return UriScheme::Builtin;
51 if (scheme == "generated") return UriScheme::Generated;
52 if (scheme == "memory") return UriScheme::Memory;
53 return std::nullopt;
54}
55
56[[nodiscard]] bool isKnownScheme(UriScheme scheme) noexcept {
57 switch (scheme) {
62 case UriScheme::Memory: return true;
63 }
64 return false;
65}
66
67[[nodiscard]] bool validPath(std::string_view path) noexcept {
68 if (path.empty() || path.front() == '/' || path.back() == '/') return false;
69 char previous = '\0';
70 std::size_t segmentStart = 0;
71 for (std::size_t i = 0; i < path.size(); ++i) {
72 const char value = path[i];
73 if (!isPathCharacter(value) || (value == '/' && previous == '/')) return false;
74 if (value == '/') {
75 const auto segment = path.substr(segmentStart, i - segmentStart);
76 if (segment == "." || segment == ".." || segment.empty()) return false;
77 segmentStart = i + 1;
78 }
80 }
81 const auto lastSegment = path.substr(segmentStart);
82 return lastSegment != "." && lastSegment != ".." && !lastSegment.empty();
83}
84
85[[nodiscard]] bool validQueryOrFragment(std::string_view value) noexcept {
86 if (value.empty()) return false;
87 return std::all_of(value.begin(), value.end(), isQueryOrFragmentCharacter);
88}
89
90[[nodiscard]] Result<Uri> parseUri(std::string_view text) {
91 const auto delimiter = text.find("://");
92 if (delimiter == std::string_view::npos || delimiter == 0) {
93 return uriFailure("resource URI must use scheme://path and cannot be a bare path", text);
94 }
95 for (std::size_t i = 0; i < delimiter; ++i) {
96 if (!isSchemeCharacter(text[i], i == 0)) {
97 return uriFailure("resource URI contains an invalid scheme", text);
98 }
99 }
100
101 std::string scheme(text.substr(0, delimiter));
102 std::transform(scheme.begin(), scheme.end(), scheme.begin(),
103 [](unsigned char value) { return static_cast<char>(std::tolower(value)); });
104 const auto schemeKind = schemeFromText(scheme);
105 if (!schemeKind) return uriFailure("unsupported resource URI scheme", text);
106
107 std::string_view remainder = text.substr(delimiter + 3);
108 const auto fragmentDelimiter = remainder.find('#');
109 std::string_view fragment;
110 if (fragmentDelimiter != std::string_view::npos) {
111 fragment = remainder.substr(fragmentDelimiter + 1);
112 remainder = remainder.substr(0, fragmentDelimiter);
113 if (!validQueryOrFragment(fragment)) return uriFailure("URI fragment is empty or invalid", text);
114 }
115
116 const auto queryDelimiter = remainder.find('?');
117 std::string_view query;
118 if (queryDelimiter != std::string_view::npos) {
119 query = remainder.substr(queryDelimiter + 1);
120 remainder = remainder.substr(0, queryDelimiter);
121 if (!validQueryOrFragment(query)) return uriFailure("URI query is empty or invalid", text);
122 }
123 if (!validPath(remainder)) return uriFailure("URI path is empty, absolute, or invalid", text);
124
125 return Uri::fromParts(*schemeKind, remainder, query, fragment);
126}
127
128} // namespace
129
130const char* uriSchemeName(UriScheme scheme) noexcept {
131 switch (scheme) {
132 case UriScheme::Asset: return "asset";
133 case UriScheme::Project: return "project";
134 case UriScheme::Builtin: return "builtin";
135 case UriScheme::Generated: return "generated";
136 case UriScheme::Memory: return "memory";
137 }
138 return "unknown";
139}
140
141Uri::Uri(UriScheme scheme, std::string path, std::string query, std::string fragment)
142 : scheme_(scheme), path_(std::move(path)), query_(std::move(query)), fragment_(std::move(fragment)) {
143 formatted_.reserve(3 + path_.size() + query_.size() + fragment_.size() + 16);
144 formatted_ += uriSchemeName(scheme_);
145 formatted_ += "://";
146 formatted_ += path_;
147 if (!query_.empty()) {
148 formatted_ += '?';
149 formatted_ += query_;
150 }
151 if (!fragment_.empty()) {
152 formatted_ += '#';
153 formatted_ += fragment_;
154 }
155}
156
157Result<Uri> Uri::parse(std::string_view text) { return parseUri(text); }
158
159Result<Uri> Uri::fromParts(UriScheme scheme, std::string_view path, std::string_view query, std::string_view fragment) {
160 if (!isKnownScheme(scheme)) return uriFailure("unknown resource URI scheme", path);
161 if (!validPath(path)) return uriFailure("URI path is empty, absolute, or invalid", path);
162 if ((!query.empty() && !validQueryOrFragment(query)) || (!fragment.empty() && !validQueryOrFragment(fragment))) {
163 return uriFailure("URI query or fragment is invalid", path);
164 }
165 return Result<Uri>::success(Uri(scheme, std::string(path), std::string(query), std::string(fragment)));
166}
167
168Result<ResourceUri> ResourceUri::parse(std::string_view text) {
169 auto parsed = Uri::parse(text);
170 if (!parsed.ok()) {
171 const auto* diagnostic = parsed.error();
172 return resourceUriFailure(diagnostic ? diagnostic->message() : "invalid resource URI", text);
173 }
174 return Result<ResourceUri>::success(ResourceUri(std::move(parsed).takeValue()));
175}
176
177Result<ResourceUri> ResourceUri::fromParts(UriScheme scheme, std::string_view path, std::string_view query,
178 std::string_view fragment) {
179 auto parsed = Uri::fromParts(scheme, path, query, fragment);
180 if (!parsed.ok()) {
181 const auto* diagnostic = parsed.error();
182 return resourceUriFailure(diagnostic ? diagnostic->message() : "invalid resource URI", path);
183 }
184 return Result<ResourceUri>::success(ResourceUri(std::move(parsed).takeValue()));
185}
186
187Result<ResourceUri> ResourceUri::fromLegacyProjectPath(std::string_view path) {
188 if (path.find("://") != std::string_view::npos) {
189 return resourceUriFailure("legacy path facade does not reinterpret URI-looking input", path);
190 }
191 std::string normalized(path);
192 std::replace(normalized.begin(), normalized.end(), '\\', '/');
193 while (normalized.size() >= 2 && normalized[0] == '.' && normalized[1] == '/') {
194 normalized.erase(0, 2);
195 }
196 return fromParts(UriScheme::Project, normalized);
197}
198
199} // namespace eve
double value
std::string message
std::int32_t first
std::string text
graphics::Canvas * previous
std::string path
Definition PlayHost.cpp:110
Strict, canonical resource URI values shared by engine modules.
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
A semantically named resource locator.
Definition Uri.h:101
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
An immutable, canonical URI value.
Definition Uri.h:44
static Result< Uri > fromParts(UriScheme scheme, std::string_view path, std::string_view query={}, std::string_view fragment={})
Construct a URI from validated components.
Definition Uri.cpp:159
bool validPath(std::string_view path, const AssetImportLimits &limits)
Valid path.
I * query()
Queries .
Definition Capability.h:88
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
UriScheme
URI schemes understood by the resource layer.
Definition Uri.h:25
const char * uriSchemeName(UriScheme scheme) noexcept
Return the stable lower-case spelling of a resource URI scheme.
Definition Uri.cpp:130