nix-scope-plugin/scope.cc
2026-02-25 20:27:08 +11:00

52 lines
1.6 KiB
C++

#include <nix/expr/primops.hh>
#include <nix/expr/eval.hh>
#include <nix/expr/eval-inline.hh>
#include <nix/expr/nixexpr.hh>
#include <nix/expr/attr-set.hh>
using namespace nix;
static void prim_scope(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
std::vector<Symbol> path;
state.forceStringNoCtx(*args[0], pos, "'scope' path argument");
auto s = std::string(args[0]->string_view());
if (s.empty())
state.error<TypeError>("'scope' requires a non-empty path")
.atPos(pos)
.debugThrow();
size_t start = 0;
while (start < s.size()) {
auto dot = s.find('.', start);
if (dot == std::string::npos) {
path.push_back(state.symbols.create(s.substr(start)));
break;
}
path.push_back(state.symbols.create(s.substr(start, dot - start)));
start = dot + 1;
}
// Build nested attrset from inside out
// Given path [a, b, c] and value v: { a = { b = { c = v; }; }; }
Value * inner = args[1];
for (auto it = path.rbegin(); it != path.rend(); ++it) {
auto attrs = state.buildBindings(1);
attrs.insert(*it, inner);
Value * wrapper = state.allocValue();
wrapper->mkAttrs(attrs.finish());
inner = wrapper;
}
v = *inner;
}
static RegisterPrimOp rp({
.name = "scope",
.args = {"path", "value"},
.doc = R"(
Construct a nested attribute set from a dotted string path and a value.
Example: `scope "a.b.c" 42` evaluates to `{ a = { b = { c = 42; }; }; }`.
)",
.fun = prim_scope,
});