11#include "llvm/ADT/StringExtras.h"
12#include "llvm/ADT/Twine.h"
13#include "llvm/Support/Error.h"
14#include "llvm/Support/Path.h"
23bool isWindowsPath(llvm::StringRef
Path) {
24 return Path.size() > 1 && llvm::isAlpha(
Path[0]) &&
Path[1] ==
':';
27bool isNetworkPath(llvm::StringRef
Path) {
29 llvm::sys::path::is_separator(
Path[0]);
36class FileSystemScheme :
public URIScheme {
38 llvm::Expected<std::string>
39 getAbsolutePath(llvm::StringRef Authority, llvm::StringRef Body,
40 llvm::StringRef )
const override {
41 if (!Body.starts_with(
"/"))
42 return error(
"File scheme: expect body to be an absolute path starting "
45 llvm::SmallString<128>
Path;
46 if (!Authority.empty()) {
48 (
"//" + Authority).toVector(
Path);
49 }
else if (isWindowsPath(Body.substr(1))) {
51 Body.consume_front(
"/");
54 llvm::sys::path::native(
Path);
55 return std::string(
Path);
59 uriFromAbsolutePath(llvm::StringRef AbsolutePath)
const override {
61 llvm::StringRef Authority;
62 llvm::StringRef
Root = llvm::sys::path::root_name(AbsolutePath);
63 if (isNetworkPath(
Root)) {
65 Authority =
Root.drop_front(2);
66 AbsolutePath.consume_front(
Root);
67 }
else if (isWindowsPath(
Root)) {
71 Body += llvm::sys::path::convert_to_slash(AbsolutePath);
72 return URI(
"file", Authority, Body);
76llvm::Expected<std::unique_ptr<URIScheme>>
77findSchemeByName(llvm::StringRef Scheme) {
79 return std::make_unique<FileSystemScheme>();
81 for (
const auto &URIScheme : URISchemeRegistry::entries()) {
82 if (URIScheme.getName() != Scheme)
84 return URIScheme.instantiate();
86 return error(
"Can't find scheme: {0}", Scheme);
89bool shouldEscape(
unsigned char C) {
91 if ((
C >=
'a' &&
C <=
'z') || (
C >=
'A' &&
C <=
'Z') ||
92 (
C >=
'0' &&
C <=
'9'))
111void percentEncode(llvm::StringRef Content, std::string &
Out) {
112 for (
unsigned char C : Content)
113 if (shouldEscape(
C)) {
115 Out.push_back(llvm::hexdigit(
C / 16));
116 Out.push_back(llvm::hexdigit(
C % 16));
123std::string percentDecode(llvm::StringRef Content) {
125 for (
auto I = Content.begin(),
E = Content.end(); I !=
E; ++I) {
130 if (*I ==
'%' && I + 2 < Content.end() && llvm::isHexDigit(*(I + 1)) &&
131 llvm::isHexDigit(*(I + 2))) {
132 Result.push_back(llvm::hexFromNibbles(*(I + 1), *(I + 2)));
135 Result.push_back(*I);
140bool isValidScheme(llvm::StringRef Scheme) {
143 if (!llvm::isAlpha(Scheme[0]))
145 return llvm::all_of(llvm::drop_begin(Scheme), [](
char C) {
146 return llvm::isAlnum(
C) ||
C ==
'+' ||
C ==
'.' ||
C ==
'-';
152URI::URI(llvm::StringRef Scheme, llvm::StringRef Authority,
153 llvm::StringRef Body)
154 : Scheme(Scheme), Authority(Authority), Body(Body) {
155 assert(!Scheme.empty());
156 assert((Authority.empty() || Body.starts_with(
"/")) &&
157 "URI body must start with '/' when authority is present.");
162 percentEncode(Scheme, Result);
163 Result.push_back(
':');
164 if (Authority.empty() && Body.empty())
168 if (!Authority.empty() || llvm::StringRef(Body).starts_with(
"/")) {
170 percentEncode(Authority, Result);
172 percentEncode(Body, Result);
178 llvm::StringRef Uri = OrigUri;
180 auto Pos = Uri.find(
':');
181 if (
Pos == llvm::StringRef::npos)
182 return error(
"Scheme must be provided in URI: {0}", OrigUri);
183 auto SchemeStr = Uri.substr(0,
Pos);
184 U.Scheme = percentDecode(SchemeStr);
185 if (!isValidScheme(U.Scheme))
186 return error(
"Invalid scheme: {0} (decoded: {1})", SchemeStr, U.Scheme);
187 Uri = Uri.substr(
Pos + 1);
188 if (Uri.consume_front(
"//")) {
190 U.Authority = percentDecode(Uri.substr(0,
Pos));
191 Uri = Uri.substr(
Pos);
193 U.Body = percentDecode(Uri);
198 llvm::StringRef HintPath) {
201 return Uri.takeError();
204 return Path.takeError();
209 llvm::StringRef Scheme) {
210 if (!llvm::sys::path::is_absolute(AbsolutePath))
211 return error(
"Not a valid absolute path: {0}", AbsolutePath);
212 auto S = findSchemeByName(Scheme);
214 return S.takeError();
215 return S->get()->uriFromAbsolutePath(AbsolutePath);
219 if (!llvm::sys::path::is_absolute(AbsolutePath))
221 (
"Not a valid absolute path: " + AbsolutePath).str().c_str());
222 for (
auto &
Entry : URISchemeRegistry::entries()) {
223 auto URI =
Entry.instantiate()->uriFromAbsolutePath(AbsolutePath);
228 llvm::consumeError(
URI.takeError());
231 return std::move(*
URI);
238 auto U = FileSystemScheme().uriFromAbsolutePath(AbsolutePath);
240 llvm_unreachable(llvm::toString(U.takeError()).c_str());
241 return std::move(*U);
245 llvm::StringRef HintPath) {
246 auto S = findSchemeByName(Uri.Scheme);
248 return S.takeError();
249 return S->get()->getAbsolutePath(Uri.Authority, Uri.Body, HintPath);
253 llvm::StringRef HintPath) {
254 if (!llvm::sys::path::is_absolute(AbsPath))
255 llvm_unreachable((
"Not a valid absolute path: " + AbsPath).str().c_str());
256 for (
auto &
Entry : URISchemeRegistry::entries()) {
257 auto S =
Entry.instantiate();
258 auto U = S->uriFromAbsolutePath(AbsPath);
263 llvm::consumeError(U.takeError());
266 return S->getAbsolutePath(U->Authority, U->Body, HintPath);
269 return std::string(AbsPath);
273 auto S = findSchemeByName(Uri.Scheme);
275 return S.takeError();
276 return S->get()->getIncludeSpelling(Uri);
CompiledFragmentImpl & Out
A URI describes the location of a source file.
static llvm::Expected< std::string > includeSpelling(const URI &U)
Gets the preferred spelling of this file for #include, if there is one, e.g.
static llvm::Expected< std::string > resolvePath(llvm::StringRef AbsPath, llvm::StringRef HintPath="")
Resolves AbsPath into a canonical path of its URI, by converting AbsPath to URI and resolving the URI...
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
static URI createFile(llvm::StringRef AbsolutePath)
This creates a file:// URI for AbsolutePath. The path must be absolute.
std::string toString() const
Returns a string URI with all components percent-encoded.
static llvm::Expected< std::string > resolve(const URI &U, llvm::StringRef HintPath="")
Resolves the absolute path of U.
static llvm::Expected< URI > parse(llvm::StringRef Uri)
Parse a URI string "<scheme>:[//<authority>/]<path>".
std::string Path
A typedef to represent a file path.
llvm::Registry< URIScheme > URISchemeRegistry
By default, a "file" scheme is supported where URI paths are always absolute in the file system.
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//