-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheval.cc
More file actions
135 lines (114 loc) · 4.31 KB
/
Copy patheval.cc
File metadata and controls
135 lines (114 loc) · 4.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include "nix/cmd/command-installable-value.hh"
#include "nix/main/common-args.hh"
#include "nix/main/shared.hh"
#include "nix/store/store-api.hh"
#include "nix/expr/eval.hh"
#include "nix/expr/eval-inline.hh"
#include "nix/expr/value-to-json.hh"
#include <nlohmann/json.hpp>
using namespace nix;
namespace nix::fs {
using namespace std::filesystem;
}
struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
{
bool raw = false;
std::optional<std::string> apply;
std::optional<std::filesystem::path> writeTo;
CmdEval()
: InstallableValueCommand()
{
addFlag({
.longName = "raw",
.description = "Print strings without quotes or escaping.",
.handler = {&raw, true},
});
addFlag({
.longName = "apply",
.description = "Apply the function *expr* to each argument.",
.labels = {"expr"},
.handler = {&apply},
});
addFlag({
.longName = "write-to",
.description = "Write a string or attrset of strings to *path*.",
.labels = {"path"},
.handler = {&writeTo},
});
}
std::string description() override
{
return "evaluate a Nix expression";
}
std::string doc() override
{
return
#include "eval.md"
;
}
Category category() override
{
return catSecondary;
}
void run(ref<Store> store, ref<InstallableValue> installable) override
{
if (raw && json)
throw UsageError("--raw and --json are mutually exclusive");
auto state = getEvalState();
auto [v, pos] = installable->toValue(*state);
NixStringContext context;
if (apply) {
auto vApply = state->allocValue();
state->eval(state->parseExprFromString(*apply, state->rootPath(".")), *vApply);
auto vRes = state->allocValue();
state->callFunction(*vApply, *v, *vRes, noPos);
v = vRes;
}
if (writeTo) {
logger->stop();
if (pathExists(*writeTo))
throw Error("path '%s' already exists", writeTo->string());
[&](this const auto & recurse, Value & v, const PosIdx pos, const std::filesystem::path & path) -> void {
state->forceValue(v, pos);
if (v.type() == nString)
// FIXME: disallow strings with contexts?
writeFile(path.string(), v.string_view());
else if (v.type() == nAttrs) {
[[maybe_unused]] bool directoryCreated = std::filesystem::create_directory(path);
// Directory should not already exist
assert(directoryCreated);
for (auto & attr : *v.attrs()) {
std::string_view name = state->symbols[attr.name];
try {
if (name == "." || name == "..")
throw Error("invalid file name '%s'", name);
recurse(*attr.value, attr.pos, path / name);
} catch (Error & e) {
e.addTrace(
state->positions[attr.pos], HintFmt("while evaluating the attribute '%s'", name));
throw;
}
}
} else
state->error<TypeError>("value at '%s' is not a string or an attribute set", state->positions[pos])
.debugThrow();
}(*v, pos, *writeTo);
}
else if (raw) {
logger->stop();
writeFull(
getStandardOutput(),
state->devirtualize(
*state->coerceToString(noPos, *v, context, "while generating the eval command output"), context));
}
else if (json) {
// FIXME: use printJSON
auto j = printValueAsJSON(*state, true, *v, pos, context, false);
logger->cout("%s", state->devirtualize(outputPretty ? j.dump(2) : j.dump(), context));
}
else {
logger->cout("%s", ValuePrinter(*state, *v, PrintOptions{.force = true, .derivationPaths = true}));
}
}
};
static auto rCmdEval = registerCommand<CmdEval>("eval");