aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/primops
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2019-05-31 19:01:11 +0200
committerEelco Dolstra <edolstra@gmail.com>2019-05-31 19:01:11 +0200
commitb971e406dee82486d53737a928b0bb3b482a6c49 (patch)
tree464c8a24ea8b9804c5e162cf873942c86857ceca /src/libexpr/primops
parent63c5c91cc053cbc1fcb8d3fe71c41142c9f51bfa (diff)
Support 'dir' and other parameters in path flakerefs
Diffstat (limited to 'src/libexpr/primops')
-rw-r--r--src/libexpr/primops/flakeref.cc90
1 files changed, 49 insertions, 41 deletions
diff --git a/src/libexpr/primops/flakeref.cc b/src/libexpr/primops/flakeref.cc
index 3c805eff8..306da20fe 100644
--- a/src/libexpr/primops/flakeref.cc
+++ b/src/libexpr/primops/flakeref.cc
@@ -1,4 +1,5 @@
#include "flakeref.hh"
+#include "store-api.hh"
#include <regex>
@@ -30,10 +31,6 @@ const static std::string schemeRegex = "(?:http|https|ssh|git|file)";
const static std::string authorityRegex = "[a-zA-Z0-9._~-]*";
const static std::string segmentRegex = "[a-zA-Z0-9._~-]+";
const static std::string pathRegex = "/?" + segmentRegex + "(?:/" + segmentRegex + ")*";
-// FIXME: support escaping in query string.
-// Note: '/' is not a valid query parameter, but so what...
-const static std::string paramRegex = "[a-z]+=[/a-zA-Z0-9._-]*";
-const static std::string paramsRegex = "(?:[?](" + paramRegex + "(?:&" + paramRegex + ")*))";
// 'dir' path elements cannot start with a '.'. We also reject
// potentially dangerous characters like ';'.
@@ -41,7 +38,7 @@ const static std::string subDirElemRegex = "(?:[a-zA-Z0-9_-]+[a-zA-Z0-9._-]*)";
const static std::string subDirRegex = subDirElemRegex + "(?:/" + subDirElemRegex + ")*";
-FlakeRef::FlakeRef(const std::string & uri, bool allowRelative)
+FlakeRef::FlakeRef(const std::string & uri_, bool allowRelative)
{
// FIXME: could combine this into one regex.
@@ -50,21 +47,46 @@ FlakeRef::FlakeRef(const std::string & uri, bool allowRelative)
std::regex::ECMAScript);
static std::regex githubRegex(
- "github:(" + ownerRegex + ")/(" + repoRegex + ")(?:/" + revOrRefRegex + ")?"
- + paramsRegex + "?",
+ "github:(" + ownerRegex + ")/(" + repoRegex + ")(?:/" + revOrRefRegex + ")?",
std::regex::ECMAScript);
static std::regex uriRegex(
"((" + schemeRegex + "):" +
"(?://(" + authorityRegex + "))?" +
- "(" + pathRegex + "))" +
- paramsRegex + "?",
+ "(" + pathRegex + "))",
std::regex::ECMAScript);
static std::regex refRegex2(refRegex, std::regex::ECMAScript);
static std::regex subDirRegex2(subDirRegex, std::regex::ECMAScript);
+ auto [uri, params] = splitUriAndParams(uri_);
+
+ auto handleSubdir = [&](const std::string & name, const std::string & value) {
+ if (name == "dir") {
+ if (value != "" && !std::regex_match(value, subDirRegex2))
+ throw Error("flake '%s' has invalid subdirectory '%s'", uri, value);
+ subdir = value;
+ return true;
+ } else
+ return false;
+ };
+
+ auto handleGitParams = [&](const std::string & name, const std::string & value) {
+ if (name == "rev") {
+ if (!std::regex_match(value, revRegex))
+ throw Error("invalid Git revision '%s'", value);
+ rev = Hash(value, htSHA1);
+ } else if (name == "ref") {
+ if (!std::regex_match(value, refRegex2))
+ throw Error("invalid Git ref '%s'", value);
+ ref = value;
+ } else if (handleSubdir(name, value))
+ ;
+ else return false;
+ return true;
+ };
+
std::cmatch match;
if (std::regex_match(uri.c_str(), match, flakeRegex)) {
IsAlias d;
@@ -88,17 +110,11 @@ FlakeRef::FlakeRef(const std::string & uri, bool allowRelative)
else if (match[4].matched) {
ref = match[4];
}
- for (auto & param : tokenizeString<Strings>(match[5], "&")) {
- auto n = param.find('=');
- assert(n != param.npos);
- std::string name(param, 0, n);
- std::string value(param, n + 1);
- if (name == "dir") {
- if (value != "" && !std::regex_match(value, subDirRegex2))
- throw Error("flake '%s' has invalid subdirectory '%s'", uri, value);
- subdir = value;
- } else
- throw Error("invalid Git flake reference parameter '%s', in '%s'", name, uri);
+ for (auto & param : params) {
+ if (handleSubdir(param.first, param.second))
+ ;
+ else
+ throw Error("invalid Git flakeref parameter '%s', in '%s'", param.first, uri);
}
data = d;
}
@@ -108,26 +124,12 @@ FlakeRef::FlakeRef(const std::string & uri, bool allowRelative)
{
IsGit d;
d.uri = match[1];
- for (auto & param : tokenizeString<Strings>(match[5], "&")) {
- auto n = param.find('=');
- assert(n != param.npos);
- std::string name(param, 0, n);
- std::string value(param, n + 1);
- if (name == "rev") {
- if (!std::regex_match(value, revRegex))
- throw Error("invalid Git revision '%s'", value);
- rev = Hash(value, htSHA1);
- } else if (name == "ref") {
- if (!std::regex_match(value, refRegex2))
- throw Error("invalid Git ref '%s'", value);
- ref = value;
- } else if (name == "dir") {
- if (value != "" && !std::regex_match(value, subDirRegex2))
- throw Error("flake '%s' has invalid subdirectory '%s'", uri, value);
- subdir = value;
- } else
+ for (auto & param : params) {
+ if (handleGitParams(param.first, param.second))
+ ;
+ else
// FIXME: should probably pass through unknown parameters
- throw Error("invalid Git flake reference parameter '%s', in '%s'", name, uri);
+ throw Error("invalid Git flakeref parameter '%s', in '%s'", param.first, uri);
}
if (rev && !ref)
throw Error("flake URI '%s' lacks a Git ref", uri);
@@ -138,6 +140,12 @@ FlakeRef::FlakeRef(const std::string & uri, bool allowRelative)
IsPath d;
d.path = allowRelative ? absPath(uri) : canonPath(uri);
data = d;
+ for (auto & param : params) {
+ if (handleGitParams(param.first, param.second))
+ ;
+ else
+ throw Error("invalid Git flakeref parameter '%s', in '%s'", param.first, uri);
+ }
}
else
@@ -165,10 +173,10 @@ std::string FlakeRef::to_string() const
}
else if (auto refData = std::get_if<FlakeRef::IsPath>(&data)) {
- assert(subdir == "");
+ string = refData->path;
if (ref) addParam("ref", *ref);
if (rev) addParam("rev", rev->gitRev());
- return refData->path;
+ if (subdir != "") addParam("dir", subdir);
}
else if (auto refData = std::get_if<FlakeRef::IsGitHub>(&data)) {