Add a toJSON primop
This commit is contained in:
parent
285df765b9
commit
77c13cdf56
10 changed files with 179 additions and 24 deletions
|
@ -750,6 +750,18 @@ in foo</programlisting>
|
|||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.toJSON</function> <replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return a string containing a JSON representation
|
||||
of <replaceable>e</replaceable>. Strings, integers, booleans,
|
||||
nulls and lists are mapped to their JSON equivalents. Sets
|
||||
(except derivations) are represented as objects. Derivations are
|
||||
translated to a JSON string containing the derivation’s output
|
||||
path. Paths are copied to the store and represented as a JSON
|
||||
string of the resulting store path.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><function>builtins.toPath</function> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Convert the string value
|
||||
|
|
|
@ -13,6 +13,11 @@
|
|||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>New built-in function: <function>builtins.toJSON</function>,
|
||||
which returns a JSON representation of a value.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem><para><command>nix-setuid-helper</command> is
|
||||
gone.</para></listitem>
|
||||
|
||||
|
|
|
@ -2,13 +2,13 @@ pkglib_LTLIBRARIES = libexpr.la
|
|||
|
||||
libexpr_la_SOURCES = \
|
||||
nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \
|
||||
get-drvs.cc attr-path.cc value-to-xml.cc common-opts.cc \
|
||||
names.cc
|
||||
get-drvs.cc attr-path.cc value-to-xml.cc value-to-json.cc \
|
||||
common-opts.cc names.cc
|
||||
|
||||
pkginclude_HEADERS = \
|
||||
nixexpr.hh eval.hh eval-inline.hh lexer-tab.hh parser-tab.hh \
|
||||
get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \
|
||||
names.hh symbol-table.hh value.hh
|
||||
get-drvs.hh attr-path.hh value-to-xml.hh value-to-json.hh \
|
||||
common-opts.hh names.hh symbol-table.hh value.hh
|
||||
|
||||
libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
|
||||
../boost/format/libformat.la @BDW_GC_LIBS@
|
||||
|
|
|
@ -1163,26 +1163,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
|
|||
|
||||
if (v.type == tPath) {
|
||||
Path path(canonPath(v.path));
|
||||
|
||||
if (!copyToStore) return path;
|
||||
|
||||
if (nix::isDerivation(path))
|
||||
throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
|
||||
|
||||
Path dstPath;
|
||||
if (srcToStore[path] != "")
|
||||
dstPath = srcToStore[path];
|
||||
else {
|
||||
dstPath = settings.readOnlyMode
|
||||
? computeStorePathForPath(path).first
|
||||
: store->addToStore(path, true, htSHA256, defaultPathFilter, repair);
|
||||
srcToStore[path] = dstPath;
|
||||
printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
|
||||
% path % dstPath);
|
||||
}
|
||||
|
||||
context.insert(dstPath);
|
||||
return dstPath;
|
||||
return copyToStore ? copyPathToStore(context, path) : path;
|
||||
}
|
||||
|
||||
if (v.type == tAttrs) {
|
||||
|
@ -1218,6 +1199,28 @@ string EvalState::coerceToString(Value & v, PathSet & context,
|
|||
}
|
||||
|
||||
|
||||
string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||
{
|
||||
if (nix::isDerivation(path))
|
||||
throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
|
||||
|
||||
Path dstPath;
|
||||
if (srcToStore[path] != "")
|
||||
dstPath = srcToStore[path];
|
||||
else {
|
||||
dstPath = settings.readOnlyMode
|
||||
? computeStorePathForPath(path).first
|
||||
: store->addToStore(path, true, htSHA256, defaultPathFilter, repair);
|
||||
srcToStore[path] = dstPath;
|
||||
printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
|
||||
% path % dstPath);
|
||||
}
|
||||
|
||||
context.insert(dstPath);
|
||||
return dstPath;
|
||||
}
|
||||
|
||||
|
||||
Path EvalState::coerceToPath(Value & v, PathSet & context)
|
||||
{
|
||||
string path = coerceToString(v, context, false, false);
|
||||
|
|
|
@ -181,6 +181,8 @@ public:
|
|||
string coerceToString(Value & v, PathSet & context,
|
||||
bool coerceMore = false, bool copyToStore = true);
|
||||
|
||||
string copyPathToStore(PathSet & context, const Path & path);
|
||||
|
||||
/* Path coercion. Converts strings, paths and derivations to a
|
||||
path. The result is guaranteed to be a canonicalised, absolute
|
||||
path. Nothing is copied to the store. */
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "util.hh"
|
||||
#include "archive.hh"
|
||||
#include "value-to-xml.hh"
|
||||
#include "value-to-json.hh"
|
||||
#include "names.hh"
|
||||
#include "eval-inline.hh"
|
||||
|
||||
|
@ -647,6 +648,18 @@ static void prim_toXML(EvalState & state, Value * * args, Value & v)
|
|||
}
|
||||
|
||||
|
||||
/* Convert the argument (which can be any Nix expression) to a JSON
|
||||
string. Not all Nix expressions can be sensibly or completely
|
||||
represented (e.g., functions). */
|
||||
static void prim_toJSON(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
std::ostringstream out;
|
||||
PathSet context;
|
||||
printValueAsJSON(state, true, *args[0], out, context);
|
||||
mkString(v, out.str(), context);
|
||||
}
|
||||
|
||||
|
||||
/* Store a string in the Nix store as a source file that can be used
|
||||
as an input by derivations. */
|
||||
static void prim_toFile(EvalState & state, Value * * args, Value & v)
|
||||
|
@ -1259,6 +1272,7 @@ void EvalState::createBaseEnv()
|
|||
|
||||
// Creating files
|
||||
addPrimOp("__toXML", 1, prim_toXML);
|
||||
addPrimOp("__toJSON", 1, prim_toJSON);
|
||||
addPrimOp("__toFile", 2, prim_toFile);
|
||||
addPrimOp("__filterSource", 2, prim_filterSource);
|
||||
|
||||
|
|
93
src/libexpr/value-to-json.cc
Normal file
93
src/libexpr/value-to-json.cc
Normal file
|
@ -0,0 +1,93 @@
|
|||
#include "value-to-xml.hh"
|
||||
#include "xml-writer.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static void escapeJSON(std::ostream & str, const string & s)
|
||||
{
|
||||
str << "\"";
|
||||
foreach (string::const_iterator, i, s)
|
||||
if (*i == '\"' || *i == '\\') str << "\\" << *i;
|
||||
else if (*i == '\n') str << "\\n";
|
||||
else if (*i == '\r') str << "\\r";
|
||||
else if (*i == '\t') str << "\\t";
|
||||
else str << *i;
|
||||
str << "\"";
|
||||
}
|
||||
|
||||
|
||||
void printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, std::ostream & str, PathSet & context)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (strict) state.forceValue(v);
|
||||
|
||||
switch (v.type) {
|
||||
|
||||
case tInt:
|
||||
str << v.integer;
|
||||
break;
|
||||
|
||||
case tBool:
|
||||
str << (v.boolean ? "true" : "false");
|
||||
break;
|
||||
|
||||
case tString:
|
||||
copyContext(v, context);
|
||||
escapeJSON(str, v.string.s);
|
||||
break;
|
||||
|
||||
case tPath:
|
||||
escapeJSON(str, state.copyPathToStore(context, v.path));
|
||||
break;
|
||||
|
||||
case tNull:
|
||||
str << "null";
|
||||
break;
|
||||
|
||||
case tAttrs: {
|
||||
Bindings::iterator i = v.attrs->find(state.sOutPath);
|
||||
if (i == v.attrs->end()) {
|
||||
str << "{";
|
||||
StringSet names;
|
||||
foreach (Bindings::iterator, i, *v.attrs)
|
||||
names.insert(i->name);
|
||||
bool first = true;
|
||||
foreach (StringSet::iterator, i, names) {
|
||||
if (!first) str << ","; else first = false;
|
||||
Attr & a(*v.attrs->find(state.symbols.create(*i)));
|
||||
escapeJSON(str, *i);
|
||||
str << ":";
|
||||
printValueAsJSON(state, strict, *a.value, str, context);
|
||||
}
|
||||
str << "}";
|
||||
} else
|
||||
printValueAsJSON(state, strict, *i->value, str, context);
|
||||
break;
|
||||
}
|
||||
|
||||
case tList: {
|
||||
str << "[";
|
||||
bool first = true;
|
||||
for (unsigned int n = 0; n < v.list.length; ++n) {
|
||||
if (!first) str << ","; else first = false;
|
||||
printValueAsJSON(state, strict, *v.list.elems[n], str, context);
|
||||
}
|
||||
str << "]";
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw TypeError(format("cannot convert %1% to JSON") % showType(v));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
14
src/libexpr/value-to-json.hh
Normal file
14
src/libexpr/value-to-json.hh
Normal file
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include "nixexpr.hh"
|
||||
#include "eval.hh"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace nix {
|
||||
|
||||
void printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, std::ostream & out, PathSet & context);
|
||||
|
||||
}
|
1
tests/lang/eval-okay-tojson.exp
Normal file
1
tests/lang/eval-okay-tojson.exp
Normal file
|
@ -0,0 +1 @@
|
|||
"{\"a\":123,\"b\":-456,\"c\":\"foo\",\"d\":\"foo\\n\\\"bar\\\"\",\"e\":true,\"f\":false,\"g\":[1,2,3],\"h\":[\"a\",[\"b\",{\"foo\\nbar\":{}}]],\"i\":3}"
|
11
tests/lang/eval-okay-tojson.nix
Normal file
11
tests/lang/eval-okay-tojson.nix
Normal file
|
@ -0,0 +1,11 @@
|
|||
builtins.toJSON
|
||||
{ a = 123;
|
||||
b = -456;
|
||||
c = "foo";
|
||||
d = "foo\n\"bar\"";
|
||||
e = true;
|
||||
f = false;
|
||||
g = [ 1 2 3 ];
|
||||
h = [ "a" [ "b" { "foo\nbar" = {}; } ] ];
|
||||
i = 1 + 2;
|
||||
}
|
Loading…
Reference in a new issue