mirror of
https://github.com/NixOS/nix
synced 2025-06-28 09:31:16 +02:00
Split the parseFlakeRefWithFragment function
Was starting to be very complex and hard to follow. Now the different cases should be easier to understand.
This commit is contained in:
parent
50e61f579c
commit
e8113747e1
1 changed files with 155 additions and 124 deletions
|
@ -69,127 +69,130 @@ std::optional<FlakeRef> maybeParseFlakeRef(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
||||||
const std::string & url,
|
const std::string & url,
|
||||||
const std::optional<Path> & baseDir,
|
const std::optional<Path> & baseDir,
|
||||||
bool allowMissing,
|
bool allowMissing,
|
||||||
bool isFlake)
|
bool isFlake)
|
||||||
{
|
{
|
||||||
using namespace fetchers;
|
std::string path = url;
|
||||||
|
std::string fragment = "";
|
||||||
|
std::map<std::string, std::string> query;
|
||||||
|
auto pathEnd = url.find_first_of("#?");
|
||||||
|
auto fragmentStart = pathEnd;
|
||||||
|
if (pathEnd != std::string::npos && url[pathEnd] == '?') {
|
||||||
|
fragmentStart = url.find("#");
|
||||||
|
}
|
||||||
|
if (pathEnd != std::string::npos) {
|
||||||
|
path = url.substr(0, pathEnd);
|
||||||
|
}
|
||||||
|
if (fragmentStart != std::string::npos) {
|
||||||
|
fragment = percentDecode(url.substr(fragmentStart+1));
|
||||||
|
}
|
||||||
|
if (pathEnd != std::string::npos && fragmentStart != std::string::npos) {
|
||||||
|
query = decodeQuery(url.substr(pathEnd+1, fragmentStart));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseDir) {
|
||||||
|
/* Check if 'url' is a path (either absolute or relative
|
||||||
|
to 'baseDir'). If so, search upward to the root of the
|
||||||
|
repo (i.e. the directory containing .git). */
|
||||||
|
|
||||||
|
path = absPath(path, baseDir);
|
||||||
|
|
||||||
|
if (isFlake) {
|
||||||
|
|
||||||
|
if (!allowMissing && !pathExists(path + "/flake.nix")){
|
||||||
|
notice("path '%s' does not contain a 'flake.nix', searching up",path);
|
||||||
|
|
||||||
|
// Save device to detect filesystem boundary
|
||||||
|
dev_t device = lstat(path).st_dev;
|
||||||
|
bool found = false;
|
||||||
|
while (path != "/") {
|
||||||
|
if (pathExists(path + "/flake.nix")) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
} else if (pathExists(path + "/.git"))
|
||||||
|
throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path);
|
||||||
|
else {
|
||||||
|
if (lstat(path).st_dev != device)
|
||||||
|
throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path);
|
||||||
|
}
|
||||||
|
path = dirOf(path);
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
throw BadURL("could not find a flake.nix file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!S_ISDIR(lstat(path).st_mode))
|
||||||
|
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
|
||||||
|
|
||||||
|
if (!allowMissing && !pathExists(path + "/flake.nix"))
|
||||||
|
throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
|
||||||
|
|
||||||
|
auto flakeRoot = path;
|
||||||
|
std::string subdir;
|
||||||
|
|
||||||
|
while (flakeRoot != "/") {
|
||||||
|
if (pathExists(flakeRoot + "/.git")) {
|
||||||
|
auto base = std::string("git+file://") + flakeRoot;
|
||||||
|
|
||||||
|
auto parsedURL = ParsedURL{
|
||||||
|
.url = base, // FIXME
|
||||||
|
.base = base,
|
||||||
|
.scheme = "git+file",
|
||||||
|
.authority = "",
|
||||||
|
.path = flakeRoot,
|
||||||
|
.query = query,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (subdir != "") {
|
||||||
|
if (parsedURL.query.count("dir"))
|
||||||
|
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
|
||||||
|
parsedURL.query.insert_or_assign("dir", subdir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathExists(flakeRoot + "/.git/shallow"))
|
||||||
|
parsedURL.query.insert_or_assign("shallow", "1");
|
||||||
|
|
||||||
|
return std::make_pair(
|
||||||
|
FlakeRef(fetchers::Input::fromURL(parsedURL), getOr(parsedURL.query, "dir", "")),
|
||||||
|
fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
|
||||||
|
flakeRoot = dirOf(flakeRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!hasPrefix(path, "/"))
|
||||||
|
throw BadURL("flake reference '%s' is not an absolute path", url);
|
||||||
|
path = canonPath(path + "/" + getOr(query, "dir", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchers::Attrs attrs;
|
||||||
|
attrs.insert_or_assign("type", "path");
|
||||||
|
attrs.insert_or_assign("path", path);
|
||||||
|
|
||||||
|
return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(std::move(attrs)), ""), fragment);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Check if 'url' is a flake ID. This is an abbreviated syntax for
|
||||||
|
'flake:<flake-id>?ref=<ref>&rev=<rev>'. */
|
||||||
|
std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(
|
||||||
|
const std::string & url,
|
||||||
|
bool isFlake
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::smatch match;
|
||||||
|
|
||||||
static std::regex flakeRegex(
|
static std::regex flakeRegex(
|
||||||
"((" + flakeIdRegexS + ")(?:/(?:" + refAndOrRevRegex + "))?)"
|
"((" + flakeIdRegexS + ")(?:/(?:" + refAndOrRevRegex + "))?)"
|
||||||
+ "(?:#(" + queryRegex + "))?",
|
+ "(?:#(" + queryRegex + "))?",
|
||||||
std::regex::ECMAScript);
|
std::regex::ECMAScript);
|
||||||
|
|
||||||
std::smatch match;
|
|
||||||
|
|
||||||
auto parsePathFlakeRef = [&]() {
|
|
||||||
std::string path = url;
|
|
||||||
std::string fragment = "";
|
|
||||||
std::map<std::string, std::string> query = {};
|
|
||||||
auto pathEnd = url.find_first_of("#?");
|
|
||||||
auto fragmentStart = pathEnd;
|
|
||||||
if (pathEnd != std::string::npos && url[pathEnd] == '?')
|
|
||||||
fragmentStart = url.find("#");
|
|
||||||
if (pathEnd != std::string::npos) {
|
|
||||||
path = url.substr(0, pathEnd);
|
|
||||||
}
|
|
||||||
if (fragmentStart != std::string::npos) {
|
|
||||||
fragment = percentDecode(url.substr(fragmentStart+1));
|
|
||||||
}
|
|
||||||
if (fragmentStart != std::string::npos && pathEnd != std::string::npos) {
|
|
||||||
query = decodeQuery(url.substr(pathEnd+1, fragmentStart));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (baseDir) {
|
|
||||||
/* Check if 'url' is a path (either absolute or relative
|
|
||||||
to 'baseDir'). If so, search upward to the root of the
|
|
||||||
repo (i.e. the directory containing .git). */
|
|
||||||
|
|
||||||
path = absPath(path, baseDir);
|
|
||||||
|
|
||||||
if (isFlake) {
|
|
||||||
|
|
||||||
if (!allowMissing && !pathExists(path + "/flake.nix")){
|
|
||||||
notice("path '%s' does not contain a 'flake.nix', searching up",path);
|
|
||||||
|
|
||||||
// Save device to detect filesystem boundary
|
|
||||||
dev_t device = lstat(path).st_dev;
|
|
||||||
bool found = false;
|
|
||||||
while (path != "/") {
|
|
||||||
if (pathExists(path + "/flake.nix")) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
} else if (pathExists(path + "/.git"))
|
|
||||||
throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path);
|
|
||||||
else {
|
|
||||||
if (lstat(path).st_dev != device)
|
|
||||||
throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path);
|
|
||||||
}
|
|
||||||
path = dirOf(path);
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
throw BadURL("could not find a flake.nix file");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!S_ISDIR(lstat(path).st_mode))
|
|
||||||
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
|
|
||||||
|
|
||||||
if (!allowMissing && !pathExists(path + "/flake.nix"))
|
|
||||||
throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
|
|
||||||
|
|
||||||
auto flakeRoot = path;
|
|
||||||
std::string subdir;
|
|
||||||
|
|
||||||
while (flakeRoot != "/") {
|
|
||||||
if (pathExists(flakeRoot + "/.git")) {
|
|
||||||
auto base = std::string("git+file://") + flakeRoot;
|
|
||||||
|
|
||||||
auto parsedURL = ParsedURL{
|
|
||||||
.url = base, // FIXME
|
|
||||||
.base = base,
|
|
||||||
.scheme = "git+file",
|
|
||||||
.authority = "",
|
|
||||||
.path = flakeRoot,
|
|
||||||
.query = query,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (subdir != "") {
|
|
||||||
if (parsedURL.query.count("dir"))
|
|
||||||
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
|
|
||||||
parsedURL.query.insert_or_assign("dir", subdir);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pathExists(flakeRoot + "/.git/shallow"))
|
|
||||||
parsedURL.query.insert_or_assign("shallow", "1");
|
|
||||||
|
|
||||||
return std::make_pair(
|
|
||||||
FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
|
|
||||||
fragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
|
|
||||||
flakeRoot = dirOf(flakeRoot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (!hasPrefix(path, "/"))
|
|
||||||
throw BadURL("flake reference '%s' is not an absolute path", url);
|
|
||||||
path = canonPath(path + "/" + getOr(query, "dir", ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchers::Attrs attrs;
|
|
||||||
attrs.insert_or_assign("type", "path");
|
|
||||||
attrs.insert_or_assign("path", path);
|
|
||||||
|
|
||||||
return std::make_pair(FlakeRef(Input::fromAttrs(std::move(attrs)), ""), fragment);
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Check if 'url' is a flake ID. This is an abbreviated syntax for
|
|
||||||
'flake:<flake-id>?ref=<ref>&rev=<rev>'. */
|
|
||||||
|
|
||||||
if (std::regex_match(url, match, flakeRegex)) {
|
if (std::regex_match(url, match, flakeRegex)) {
|
||||||
auto parsedURL = ParsedURL{
|
auto parsedURL = ParsedURL{
|
||||||
.url = url,
|
.url = url,
|
||||||
|
@ -200,25 +203,53 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
};
|
};
|
||||||
|
|
||||||
return std::make_pair(
|
return std::make_pair(
|
||||||
FlakeRef(Input::fromURL(parsedURL), ""),
|
FlakeRef(fetchers::Input::fromURL(parsedURL, isFlake), ""),
|
||||||
percentDecode(match.str(6)));
|
percentDecode(match.str(6)));
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
return {};
|
||||||
try {
|
}
|
||||||
auto parsedURL = parseURL(url);
|
|
||||||
std::string fragment;
|
|
||||||
std::swap(fragment, parsedURL.fragment);
|
|
||||||
|
|
||||||
auto input = Input::fromURL(parsedURL, isFlake);
|
std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
|
||||||
input.parent = baseDir;
|
const std::string & url,
|
||||||
|
const std::optional<Path> & baseDir,
|
||||||
|
bool isFlake
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ParsedURL parsedURL;
|
||||||
|
try {
|
||||||
|
parsedURL = parseURL(url);
|
||||||
|
} catch (BadURL &) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
return std::make_pair(
|
std::string fragment;
|
||||||
FlakeRef(std::move(input), getOr(parsedURL.query, "dir", "")),
|
std::swap(fragment, parsedURL.fragment);
|
||||||
fragment);
|
|
||||||
} catch (BadURL &) {
|
auto input = fetchers::Input::fromURL(parsedURL, isFlake);
|
||||||
return parsePathFlakeRef();
|
input.parent = baseDir;
|
||||||
}
|
|
||||||
|
return std::make_pair(
|
||||||
|
FlakeRef(std::move(input), getOr(parsedURL.query, "dir", "")),
|
||||||
|
fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
|
const std::string & url,
|
||||||
|
const std::optional<Path> & baseDir,
|
||||||
|
bool allowMissing,
|
||||||
|
bool isFlake)
|
||||||
|
{
|
||||||
|
using namespace fetchers;
|
||||||
|
|
||||||
|
std::smatch match;
|
||||||
|
|
||||||
|
if (auto res = parseFlakeIdRef(url, isFlake)) {
|
||||||
|
return *res;
|
||||||
|
} else if (auto res = parseURLFlakeRef(url, baseDir, isFlake)) {
|
||||||
|
return *res;
|
||||||
|
} else {
|
||||||
|
return parsePathFlakeRefWithFragment(url, baseDir, allowMissing, isFlake);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue