11#include <llvm/ADT/StringExtras.h>
12#include <llvm/ADT/Twine.h>
13#include <llvm/Support/Error.h>
14#include <llvm/Support/Path.h>
21bool isWindowsPath(llvm::StringRef
Path) {
22 return Path.size() > 1 && llvm::isAlpha(
Path[0]) &&
Path[1] ==
':';
25bool isNetworkPath(llvm::StringRef
Path) {
27 llvm::sys::path::is_separator(
Path[0]);
34class FileSystemScheme :
public URIScheme {
36 llvm::Expected<std::string>
37 getAbsolutePath(llvm::StringRef Authority, llvm::StringRef Body,
38 llvm::StringRef )
const override {
39 if (!Body.starts_with(
"/"))
40 return error(
"File scheme: expect body to be an absolute path starting "
43 llvm::SmallString<128>
Path;
44 if (!Authority.empty()) {
46 (
"//" + Authority).toVector(
Path);
47 }
else if (isWindowsPath(Body.substr(1))) {
49 Body.consume_front(
"/");
52 llvm::sys::path::native(
Path);
53 return std::string(
Path);
57 uriFromAbsolutePath(llvm::StringRef AbsolutePath)
const override {
59 llvm::StringRef Authority;
60 llvm::StringRef Root = llvm::sys::path::root_name(AbsolutePath);
61 if (isNetworkPath(Root)) {
63 Authority = Root.drop_front(2);
64 AbsolutePath.consume_front(Root);
65 }
else if (isWindowsPath(Root)) {
69 Body += llvm::sys::path::convert_to_slash(AbsolutePath);
70 return URI(
"file", Authority, Body);
74llvm::Expected<std::unique_ptr<URIScheme>>
75findSchemeByName(llvm::StringRef Scheme) {
77 return std::make_unique<FileSystemScheme>();
79 for (
const auto &URIScheme : URISchemeRegistry::entries()) {
80 if (URIScheme.getName() != Scheme)
82 return URIScheme.instantiate();
84 return error(
"Can't find scheme: {0}", Scheme);
87bool shouldEscape(
unsigned char C) {
89 if ((C >=
'a' && C <=
'z') || (C >=
'A' && C <=
'Z') ||
90 (C >=
'0' && C <=
'9'))
109void percentEncode(llvm::StringRef Content, std::string &Out) {
110 for (
unsigned char C : Content)
111 if (shouldEscape(C)) {
113 Out.push_back(llvm::hexdigit(C / 16));
114 Out.push_back(llvm::hexdigit(C % 16));
121std::string percentDecode(llvm::StringRef Content) {
123 for (
auto I = Content.begin(), E = Content.end(); I != E; ++I) {
128 if (*I ==
'%' && I + 2 < Content.end() && llvm::isHexDigit(*(I + 1)) &&
129 llvm::isHexDigit(*(I + 2))) {
130 Result.push_back(llvm::hexFromNibbles(*(I + 1), *(I + 2)));
133 Result.push_back(*I);
138bool isValidScheme(llvm::StringRef Scheme) {
141 if (!llvm::isAlpha(Scheme[0]))
143 return llvm::all_of(llvm::drop_begin(Scheme), [](
char C) {
144 return llvm::isAlnum(C) || C ==
'+' || C ==
'.' || C ==
'-';
150URI::URI(llvm::StringRef Scheme, llvm::StringRef Authority,
151 llvm::StringRef Body)
152 : Scheme(Scheme), Authority(Authority), Body(Body) {
153 assert(!Scheme.empty());
154 assert((Authority.empty() || Body.starts_with(
"/")) &&
155 "URI body must start with '/' when authority is present.");
160 percentEncode(Scheme, Result);
161 Result.push_back(
':');
162 if (Authority.empty() && Body.empty())
166 if (!Authority.empty() || llvm::StringRef(Body).starts_with(
"/")) {
168 percentEncode(Authority, Result);
170 percentEncode(Body, Result);
176 llvm::StringRef Uri = OrigUri;
178 auto Pos = Uri.find(
':');
179 if (Pos == llvm::StringRef::npos)
180 return error(
"Scheme must be provided in URI: {0}", OrigUri);
181 auto SchemeStr = Uri.substr(0, Pos);
182 U.Scheme = percentDecode(SchemeStr);
183 if (!isValidScheme(U.Scheme))
184 return error(
"Invalid scheme: {0} (decoded: {1})", SchemeStr, U.Scheme);
185 Uri = Uri.substr(Pos + 1);
186 if (Uri.consume_front(
"//")) {
188 U.Authority = percentDecode(Uri.substr(0, Pos));
189 Uri = Uri.substr(Pos);
191 U.Body = percentDecode(Uri);
196 llvm::StringRef HintPath) {
199 return Uri.takeError();
202 return Path.takeError();
207 llvm::StringRef Scheme) {
208 if (!llvm::sys::path::is_absolute(AbsolutePath))
209 return error(
"Not a valid absolute path: {0}", AbsolutePath);
210 auto S = findSchemeByName(Scheme);
212 return S.takeError();
213 return S->get()->uriFromAbsolutePath(AbsolutePath);
217 if (!llvm::sys::path::is_absolute(AbsolutePath))
219 (
"Not a valid absolute path: " + AbsolutePath).str().c_str());
220 for (
auto &Entry : URISchemeRegistry::entries()) {
221 auto URI = Entry.instantiate()->uriFromAbsolutePath(AbsolutePath);
226 llvm::consumeError(
URI.takeError());
229 return std::move(*
URI);
236 auto U = FileSystemScheme().uriFromAbsolutePath(AbsolutePath);
238 llvm_unreachable(llvm::toString(U.takeError()).c_str());
239 return std::move(*U);
243 llvm::StringRef HintPath) {
244 auto S = findSchemeByName(Uri.Scheme);
246 return S.takeError();
247 return S->get()->getAbsolutePath(Uri.Authority, Uri.Body, HintPath);
251 llvm::StringRef HintPath) {
252 if (!llvm::sys::path::is_absolute(AbsPath))
253 llvm_unreachable((
"Not a valid absolute path: " + AbsPath).str().c_str());
254 for (
auto &Entry : URISchemeRegistry::entries()) {
255 auto S = Entry.instantiate();
256 auto U = S->uriFromAbsolutePath(AbsPath);
261 llvm::consumeError(U.takeError());
264 return S->getAbsolutePath(U->Authority, U->Body, HintPath);
267 return std::string(AbsPath);
271 auto S = findSchemeByName(Uri.Scheme);
273 return S.takeError();
274 return S->get()->getIncludeSpelling(Uri);
static llvm::Expected< std::string > resolve(const URI &U, llvm::StringRef HintPath="")
static llvm::Expected< URI > parse(llvm::StringRef Uri)
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
std::string toString() const
Returns a string URI with all components percent-encoded.
static llvm::Expected< std::string > includeSpelling(const URI &U)
static URI createFile(llvm::StringRef AbsolutePath)
This creates a file:// URI for AbsolutePath. The path must be absolute.
static llvm::Expected< std::string > resolvePath(llvm::StringRef AbsPath, llvm::StringRef HintPath="")
Whether current platform treats paths case insensitively.
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&...Vals)
llvm::Registry< URIScheme > URISchemeRegistry