aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2016-08-26 18:55:55 +0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2016-08-29 17:29:24 +0200
commitc0a7b84748d5e27e6804117b8a57ce71269c3c66 (patch)
tree40f22e1dd5636673f764bc1f289ab254971ca95d
parent9fa21765e7f267efcc65e1aa6ab21402ea6125ad (diff)
nix path-info: Add --json flag
Also, factor out JSON generation from value-to-json.{cc,hh}, and support producing indented JSON.
-rw-r--r--src/libexpr/value-to-json.cc55
-rw-r--r--src/libexpr/value-to-json.hh71
-rw-r--r--src/libexpr/value.hh3
-rw-r--r--src/libutil/json.cc171
-rw-r--r--src/libutil/json.hh183
-rw-r--r--src/nix-env/nix-env.cc15
-rw-r--r--src/nix/path-info.cc103
7 files changed, 472 insertions, 129 deletions
diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc
index 47ee324a6..72e413e44 100644
--- a/src/libexpr/value-to-json.cc
+++ b/src/libexpr/value-to-json.cc
@@ -1,4 +1,5 @@
#include "value-to-json.hh"
+#include "json.hh"
#include "eval-inline.hh"
#include "util.hh"
@@ -8,24 +9,8 @@
namespace nix {
-
-void escapeJSON(std::ostream & str, const string & s)
-{
- str << "\"";
- for (auto & 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 if (i >= 0 && i < 32)
- str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) i << std::dec;
- else str << i;
- str << "\"";
-}
-
-
void printValueAsJSON(EvalState & state, bool strict,
- Value & v, std::ostream & str, PathSet & context)
+ Value & v, JSONPlaceholder & out, PathSet & context)
{
checkInterrupt();
@@ -34,58 +19,58 @@ void printValueAsJSON(EvalState & state, bool strict,
switch (v.type) {
case tInt:
- str << v.integer;
+ out.write(v.integer);
break;
case tBool:
- str << (v.boolean ? "true" : "false");
+ out.write(v.boolean);
break;
case tString:
copyContext(v, context);
- escapeJSON(str, v.string.s);
+ out.write(v.string.s);
break;
case tPath:
- escapeJSON(str, state.copyPathToStore(context, v.path));
+ out.write(state.copyPathToStore(context, v.path));
break;
case tNull:
- str << "null";
+ out.write(nullptr);
break;
case tAttrs: {
Bindings::iterator i = v.attrs->find(state.sOutPath);
if (i == v.attrs->end()) {
- JSONObject json(str);
+ auto obj(out.object());
StringSet names;
for (auto & j : *v.attrs)
names.insert(j.name);
for (auto & j : names) {
Attr & a(*v.attrs->find(state.symbols.create(j)));
- json.attr(j);
- printValueAsJSON(state, strict, *a.value, str, context);
+ auto placeholder(obj.placeholder(j));
+ printValueAsJSON(state, strict, *a.value, placeholder, context);
}
} else
- printValueAsJSON(state, strict, *i->value, str, context);
+ printValueAsJSON(state, strict, *i->value, out, context);
break;
}
case tList1: case tList2: case tListN: {
- JSONList json(str);
+ auto list(out.list());
for (unsigned int n = 0; n < v.listSize(); ++n) {
- json.elem();
- printValueAsJSON(state, strict, *v.listElems()[n], str, context);
+ auto placeholder(list.placeholder());
+ printValueAsJSON(state, strict, *v.listElems()[n], placeholder, context);
}
break;
}
case tExternal:
- v.external->printValueAsJSON(state, strict, str, context);
+ v.external->printValueAsJSON(state, strict, out, context);
break;
case tFloat:
- str << v.fpoint;
+ out.write(v.fpoint);
break;
default:
@@ -93,9 +78,15 @@ void printValueAsJSON(EvalState & state, bool strict,
}
}
+void printValueAsJSON(EvalState & state, bool strict,
+ Value & v, std::ostream & str, PathSet & context)
+{
+ JSONPlaceholder out(str);
+ printValueAsJSON(state, strict, v, out, context);
+}
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
- std::ostream & str, PathSet & context) const
+ JSONPlaceholder & out, PathSet & context) const
{
throw TypeError(format("cannot convert %1% to JSON") % showType());
}
diff --git a/src/libexpr/value-to-json.hh b/src/libexpr/value-to-json.hh
index c59caf564..67fed6487 100644
--- a/src/libexpr/value-to-json.hh
+++ b/src/libexpr/value-to-json.hh
@@ -8,73 +8,12 @@
namespace nix {
-void printValueAsJSON(EvalState & state, bool strict,
- Value & v, std::ostream & out, PathSet & context);
-
-void escapeJSON(std::ostream & str, const string & s);
+class JSONPlaceholder;
-struct JSONObject
-{
- std::ostream & str;
- bool first;
- JSONObject(std::ostream & str) : str(str), first(true)
- {
- str << "{";
- }
- ~JSONObject()
- {
- str << "}";
- }
- void attr(const string & s)
- {
- if (!first) str << ","; else first = false;
- escapeJSON(str, s);
- str << ":";
- }
- void attr(const string & s, const string & t)
- {
- attr(s);
- escapeJSON(str, t);
- }
- void attr(const string & s, const char * t)
- {
- attr(s);
- escapeJSON(str, t);
- }
- void attr(const string & s, bool b)
- {
- attr(s);
- str << (b ? "true" : "false");
- }
- template<typename T>
- void attr(const string & s, const T & n)
- {
- attr(s);
- str << n;
- }
-};
+void printValueAsJSON(EvalState & state, bool strict,
+ Value & v, JSONPlaceholder & out, PathSet & context);
-struct JSONList
-{
- std::ostream & str;
- bool first;
- JSONList(std::ostream & str) : str(str), first(true)
- {
- str << "[";
- }
- ~JSONList()
- {
- str << "]";
- }
- void elem()
- {
- if (!first) str << ","; else first = false;
- }
- void elem(const string & s)
- {
- elem();
- escapeJSON(str, s);
- }
-};
+void printValueAsJSON(EvalState & state, bool strict,
+ Value & v, std::ostream & str, PathSet & context);
}
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index 62bdd9281..f5e485748 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -36,6 +36,7 @@ class Symbol;
struct Pos;
class EvalState;
class XMLWriter;
+class JSONPlaceholder;
typedef long NixInt;
@@ -73,7 +74,7 @@ class ExternalValueBase
/* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */
virtual void printValueAsJSON(EvalState & state, bool strict,
- std::ostream & str, PathSet & context) const;
+ JSONPlaceholder & out, PathSet & context) const;
/* Print the value as XML. Defaults to unevaluated */
virtual void printValueAsXML(EvalState & state, bool strict, bool location,
diff --git a/src/libutil/json.cc b/src/libutil/json.cc
new file mode 100644
index 000000000..619b1d631
--- /dev/null
+++ b/src/libutil/json.cc
@@ -0,0 +1,171 @@
+#include "json.hh"
+
+#include <iomanip>
+#include <cstring>
+
+namespace nix {
+
+void toJSON(std::ostream & str, const char * start, const char * end)
+{
+ str << '"';
+ for (auto i = start; i != end; i++)
+ if (*i == '\"' || *i == '\\') str << '\\' << *i;
+ else if (*i == '\n') str << "\\n";
+ else if (*i == '\r') str << "\\r";
+ else if (*i == '\t') str << "\\t";
+ else if (*i >= 0 && *i < 32)
+ str << "\\u" << std::setfill('0') << std::setw(4) << std::hex << (uint16_t) *i << std::dec;
+ else str << *i;
+ str << '"';
+}
+
+void toJSON(std::ostream & str, const std::string & s)
+{
+ toJSON(str, s.c_str(), s.c_str() + s.size());
+}
+
+void toJSON(std::ostream & str, const char * s)
+{
+ if (!s) str << "null"; else toJSON(str, s, s + strlen(s));
+}
+
+void toJSON(std::ostream & str, unsigned long n)
+{
+ str << n;
+}
+
+void toJSON(std::ostream & str, long n)
+{
+ str << n;
+}
+
+void toJSON(std::ostream & str, double f)
+{
+ str << f;
+}
+
+void toJSON(std::ostream & str, bool b)
+{
+ str << (b ? "true" : "false");
+}
+
+JSONWriter::JSONWriter(std::ostream & str, bool indent)
+ : state(new JSONState(str, indent))
+{
+ state->stack.push_back(this);
+}
+
+JSONWriter::JSONWriter(JSONState * state)
+ : state(state)
+{
+ state->stack.push_back(this);
+}
+
+JSONWriter::~JSONWriter()
+{
+ assertActive();
+ state->stack.pop_back();
+ if (state->stack.empty()) delete state;
+}
+
+void JSONWriter::comma()
+{
+ assertActive();
+ if (first) {
+ first = false;
+ } else {
+ state->str << ',';
+ }
+ if (state->indent) indent();
+}
+
+void JSONWriter::indent()
+{
+ state->str << '\n' << std::string(state->depth * 2, ' ');
+}
+
+void JSONList::open()
+{
+ state->depth++;
+ state->str << '[';
+}
+
+JSONList::~JSONList()
+{
+ state->depth--;
+ if (state->indent && !first) indent();
+ state->str << "]";
+}
+
+JSONList JSONList::list()
+{
+ comma();
+ return JSONList(state);
+}
+
+JSONObject JSONList::object()
+{
+ comma();
+ return JSONObject(state);
+}
+
+JSONPlaceholder JSONList::placeholder()
+{
+ comma();
+ return JSONPlaceholder(state);
+}
+
+void JSONObject::open()
+{
+ state->depth++;
+ state->str << '{';
+}
+
+JSONObject::~JSONObject()
+{
+ state->depth--;
+ if (state->indent && !first) indent();
+ state->str << "}";
+}
+
+void JSONObject::attr(const std::string & s)
+{
+ comma();
+ toJSON(state->str, s);
+ state->str << ':';
+ if (state->indent) state->str << ' ';
+}
+
+JSONList JSONObject::list(const std::string & name)
+{
+ attr(name);
+ return JSONList(state);
+}
+
+JSONObject JSONObject::object(const std::string & name)
+{
+ attr(name);
+ return JSONObject(state);
+}
+
+JSONPlaceholder JSONObject::placeholder(const std::string & name)
+{
+ attr(name);
+ return JSONPlaceholder(state);
+}
+
+JSONList JSONPlaceholder::list()
+{
+ assertValid();
+ first = false;
+ return JSONList(state);
+}
+
+JSONObject JSONPlaceholder::object()
+{
+ assertValid();
+ first = false;
+ return JSONObject(state);
+}
+
+}
diff --git a/src/libutil/json.hh b/src/libutil/json.hh
new file mode 100644
index 000000000..852000a51
--- /dev/null
+++ b/src/libutil/json.hh
@@ -0,0 +1,183 @@
+#pragma once
+
+#include <iostream>
+#include <vector>
+#include <cassert>
+
+namespace nix {
+
+void toJSON(std::ostream & str, const char * start, const char * end);
+void toJSON(std::ostream & str, const std::string & s);
+void toJSON(std::ostream & str, const char * s);
+void toJSON(std::ostream & str, unsigned long n);
+void toJSON(std::ostream & str, long n);
+void toJSON(std::ostream & str, double f);
+void toJSON(std::ostream & str, bool b);
+
+class JSONWriter
+{
+protected:
+
+ struct JSONState
+ {
+ std::ostream & str;
+ bool indent;
+ size_t depth = 0;
+ std::vector<JSONWriter *> stack;
+ JSONState(std::ostream & str, bool indent) : str(str), indent(indent) { }
+ ~JSONState()
+ {
+ assert(stack.empty());
+ }
+ };
+
+ JSONState * state;
+
+ bool first = true;
+
+ JSONWriter(std::ostream & str, bool indent);
+
+ JSONWriter(JSONState * state);
+
+ ~JSONWriter();
+
+ void assertActive()
+ {
+ assert(!state->stack.empty() && state->stack.back() == this);
+ }
+
+ void comma();
+
+ void indent();
+};
+
+class JSONObject;
+class JSONPlaceholder;
+
+class JSONList : JSONWriter
+{
+private:
+
+ friend class JSONObject;
+ friend class JSONPlaceholder;
+
+ void open();
+
+ JSONList(JSONState * state)
+ : JSONWriter(state)
+ {
+ open();
+ }
+
+public:
+
+ JSONList(std::ostream & str, bool indent = false)
+ : JSONWriter(str, indent)
+ {
+ open();
+ }
+
+ ~JSONList();
+
+ template<typename T>
+ JSONList & elem(const T & v)
+ {
+ comma();
+ toJSON(state->str, v);
+ return *this;
+ }
+
+ JSONList list();
+
+ JSONObject object();
+
+ JSONPlaceholder placeholder();
+};
+
+class JSONObject : JSONWriter
+{
+private:
+
+ friend class JSONList;
+ friend class JSONPlaceholder;
+
+ void open();
+
+ JSONObject(JSONState * state)
+ : JSONWriter(state)
+ {
+ open();
+ }
+
+ void attr(const std::string & s);
+
+public:
+
+ JSONObject(std::ostream & str, bool indent = false)
+ : JSONWriter(str, indent)
+ {
+ open();
+ }
+
+ ~JSONObject();
+
+ template<typename T>
+ JSONObject & attr(const std::string & name, const T & v)
+ {
+ attr(name);
+ toJSON(state->str, v);
+ return *this;
+ }
+
+ JSONList list(const std::string & name);
+
+ JSONObject object(const std::string & name);
+
+ JSONPlaceholder placeholder(const std::string & name);
+};
+
+class JSONPlaceholder : JSONWriter
+{
+
+private:
+
+ friend class JSONList;
+ friend class JSONObject;
+
+ JSONPlaceholder(JSONState * state)
+ : JSONWriter(state)
+ {
+ }
+
+ void assertValid()
+ {
+ assertActive();
+ assert(first);
+ }
+
+public:
+
+ JSONPlaceholder(std::ostream & str, bool indent = false)
+ : JSONWriter(str, indent)
+ {
+ }
+
+ ~JSONPlaceholder()
+ {
+ assert(!first || std::uncaught_exception());
+ }
+
+ template<typename T>
+ void write(const T & v)
+ {
+ assertValid();
+ first = false;
+ toJSON(state->str, v);
+ }
+
+ JSONList list();
+
+ JSONObject object();
+};
+
+}
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index 955a24661..6a557e8ac 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -10,6 +10,7 @@
#include "store-api.hh"
#include "user-env.hh"
#include "util.hh"
+#include "json.hh"
#include "value-to-json.hh"
#include "xml-writer.hh"
@@ -860,26 +861,24 @@ static VersionDiff compareVersionAgainstSet(
static void queryJSON(Globals & globals, vector<DrvInfo> & elems)
{
- JSONObject topObj(cout);
+ JSONObject topObj(cout, true);
for (auto & i : elems) {
- topObj.attr(i.attrPath);
- JSONObject pkgObj(cout);
+ JSONObject pkgObj = topObj.object(i.attrPath);
pkgObj.attr("name", i.name);
pkgObj.attr("system", i.system);
- pkgObj.attr("meta");
- JSONObject metaObj(cout);
+ JSONObject metaObj = pkgObj.object("meta");
StringSet metaNames = i.queryMetaNames();
for (auto & j : metaNames) {
- metaObj.attr(j);
+ auto placeholder = metaObj.placeholder(j);
Value * v = i.queryMeta(j);
if (!v) {
printMsg(lvlError, format("derivation ‘%1%’ has invalid meta attribute ‘%2%’") % i.name % j);
- cout << "null";
+ placeholder.write(nullptr);
} else {
PathSet context;
- printValueAsJSON(*globals.state, true, *v, cout, context);
+ printValueAsJSON(*globals.state, true, *v, placeholder, context);
}
}
}
diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc
index dca22240b..b25e60db6 100644
--- a/src/nix/path-info.cc
+++ b/src/nix/path-info.cc
@@ -2,6 +2,10 @@
#include "shared.hh"
#include "store-api.hh"
+
+#include "json.hh"
+
+
#include <iomanip>
#include <algorithm>
@@ -12,12 +16,14 @@ struct CmdPathInfo : StorePathsCommand
bool showSize = false;
bool showClosureSize = false;
bool showSigs = false;
+ bool json = false;
CmdPathInfo()
{
mkFlag('s', "size", "print size of the NAR dump of each path", &showSize);
mkFlag('S', "closure-size", "print sum size of the NAR dumps of the closure of each path", &showClosureSize);
mkFlag(0, "sigs", "show signatures", &showSigs);
+ mkFlag(0, "json", "produce JSON output", &json);
}
std::string name() override
@@ -41,6 +47,10 @@ struct CmdPathInfo : StorePathsCommand
"To check the existence of a path in a binary cache:",
"nix path-info -r /nix/store/7qvk5c91...-geeqie-1.1 --store https://cache.nixos.org/"
},
+ Example{
+ "To print the 10 most recently added paths (using --json and the jq(1) command):",
+ "nix path-info --all --json | jq -r 'sort_by(.registrationTime)[-11:-1][].path'"
+ },
};
}
@@ -50,36 +60,85 @@ struct CmdPathInfo : StorePathsCommand
for (auto & storePath : storePaths)
pathLen = std::max(pathLen, storePath.size());
- for (auto storePath : storePaths) {
- auto info = store->queryPathInfo(storePath);
- storePath = info->path; // FIXME: screws up padding
+ auto getClosureSize = [&](const Path & storePath) {
+ size_t totalSize = 0;
+ PathSet closure;
+ store->computeFSClosure(storePath, closure, false, false);
+ for (auto & p : closure)
+ totalSize += store->queryPathInfo(p)->narSize;
+ return totalSize;
+ };
- std::cout << storePath << std::string(std::max(0, (int) pathLen - (int) storePath.size()), ' ');
+ if (json) {
+ JSONList jsonRoot(std::cout, true);
- if (showSize) {
- std::cout << '\t' << std::setw(11) << info->narSize;
- }
+ for (auto storePath : storePaths) {
+ auto info = store->queryPathInfo(storePath);
+ storePath = info->path;
+
+ auto jsonPath = jsonRoot.object();
+ jsonPath
+ .attr("path", storePath)
+ .attr("narHash", info->narHash.to_string())
+ .attr("narSize", info->narSize);
+
+ if (showClosureSize)
+ jsonPath.attr("closureSize", getClosureSize(storePath));
+
+ if (info->deriver != "")
+ jsonPath.attr("deriver", info->deriver);
+
+ {
+ auto jsonRefs = jsonPath.list("references");
+ for (auto & ref : info->references)
+ jsonRefs.elem(ref);
+ }
- if (showClosureSize) {
- size_t totalSize = 0;
- PathSet closure;
- store->computeFSClosure(storePath, closure, false, false);
- for (auto & p : closure)
- totalSize += store->queryPathInfo(p)->narSize;
- std::cout << '\t' << std::setw(11) << totalSize;
+ if (info->registrationTime)
+ jsonPath.attr("registrationTime", info->registrationTime);
+
+ if (info->ultimate)
+ jsonPath.attr("ultimate", info->ultimate);
+
+ if (info->ca != "")
+ jsonPath.attr("ca", info->ca);
+
+ if (!info->sigs.empty()) {
+ auto jsonSigs = jsonPath.list("signatures");
+ for (auto & sig : info->sigs)
+ jsonSigs.elem(sig);
+ }
}
+ }
+
+ else {
+
+ for (auto storePath : storePaths) {
+ auto info = store->queryPathInfo(storePath);
+ storePath = info->path; // FIXME: screws up padding
- if (showSigs) {
- std::cout << '\t';
- Strings ss;
- if (info->ultimate) ss.push_back("ultimate");
- if (info->ca != "") ss.push_back("ca:" + info->ca);
- for (auto & sig : info->sigs) ss.push_back(sig);
- std::cout << concatStringsSep(" ", ss);
+ std::cout << storePath << std::string(std::max(0, (int) pathLen - (int) storePath.size()), ' ');
+
+ if (showSize)
+ std::cout << '\t' << std::setw(11) << info->narSize;
+
+ if (showClosureSize)
+ std::cout << '\t' << std::setw(11) << getClosureSize(storePath);
+
+ if (showSigs) {
+ std::cout << '\t';
+ Strings ss;
+ if (info->ultimate) ss.push_back("ultimate");
+ if (info->ca != "") ss.push_back("ca:" + info->ca);
+ for (auto & sig : info->sigs) ss.push_back(sig);
+ std::cout << concatStringsSep(" ", ss);
+ }
+
+ std::cout << std::endl;
}
- std::cout << std::endl;
}
+
}
};