about summary refs log tree commit diff
path: root/third_party/nix/src/libexpr/attr-path.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/nix/src/libexpr/attr-path.cc')
-rw-r--r--third_party/nix/src/libexpr/attr-path.cc109
1 files changed, 109 insertions, 0 deletions
diff --git a/third_party/nix/src/libexpr/attr-path.cc b/third_party/nix/src/libexpr/attr-path.cc
new file mode 100644
index 000000000000..86ebeec2fb15
--- /dev/null
+++ b/third_party/nix/src/libexpr/attr-path.cc
@@ -0,0 +1,109 @@
+#include "libexpr/attr-path.hh"
+
+#include <absl/strings/numbers.h>
+
+#include "libexpr/eval-inline.hh"
+#include "libutil/util.hh"
+
+namespace nix {
+
+static Strings parseAttrPath(const std::string& s) {
+  Strings res;
+  std::string cur;
+  std::string::const_iterator i = s.begin();
+  while (i != s.end()) {
+    if (*i == '.') {
+      res.push_back(cur);
+      cur.clear();
+    } else if (*i == '"') {
+      ++i;
+      while (true) {
+        if (i == s.end()) {
+          throw Error(format("missing closing quote in selection path '%1%'") %
+                      s);
+        }
+        if (*i == '"') {
+          break;
+        }
+        cur.push_back(*i++);
+      }
+    } else {
+      cur.push_back(*i);
+    }
+    ++i;
+  }
+  if (!cur.empty()) {
+    res.push_back(cur);
+  }
+  return res;
+}
+
+Value* findAlongAttrPath(EvalState& state, const std::string& attrPath,
+                         Bindings* autoArgs, Value& vIn) {
+  Strings tokens = parseAttrPath(attrPath);
+
+  Error attrError =
+      Error(format("attribute selection path '%1%' does not match expression") %
+            attrPath);
+
+  Value* v = &vIn;
+
+  for (auto& attr : tokens) {
+    /* Is i an index (integer) or a normal attribute name? */
+    enum { apAttr, apIndex } apType = apAttr;
+    unsigned int attrIndex;
+    if (absl::SimpleAtoi(attr, &attrIndex)) {
+      apType = apIndex;
+    }
+
+    /* Evaluate the expression. */
+    Value* vNew = state.allocValue();
+    state.autoCallFunction(autoArgs, *v, *vNew);
+    v = vNew;
+    state.forceValue(*v);
+
+    /* It should evaluate to either a set or an expression,
+       according to what is specified in the attrPath. */
+
+    if (apType == apAttr) {
+      if (v->type != tAttrs) {
+        throw TypeError(format("the expression selected by the selection path "
+                               "'%1%' should be a set but is %2%") %
+                        attrPath % showType(*v));
+      }
+
+      if (attr.empty()) {
+        throw Error(format("empty attribute name in selection path '%1%'") %
+                    attrPath);
+      }
+
+      Bindings::iterator a = v->attrs->find(state.symbols.Create(attr));
+      if (a == v->attrs->end()) {
+        throw Error(
+            format("attribute '%1%' in selection path '%2%' not found") % attr %
+            attrPath);
+      }
+      v = &*(a->second).value;
+    }
+
+    else if (apType == apIndex) {
+      if (!v->isList()) {
+        throw TypeError(format("the expression selected by the selection path "
+                               "'%1%' should be a list but is %2%") %
+                        attrPath % showType(*v));
+      }
+
+      if (attrIndex >= v->listSize()) {
+        throw Error(
+            format("list index %1% in selection path '%2%' is out of range") %
+            attrIndex % attrPath);
+      }
+
+      v = (*v->list)[attrIndex];
+    }
+  }
+
+  return v;
+}
+
+}  // namespace nix