nix path-info: Add --json flag
Also, factor out JSON generation from value-to-json.{cc,hh}, and
support producing indented JSON.
			
			
This commit is contained in:
		
							parent
							
								
									9fa21765e7
								
							
						
					
					
						commit
						c0a7b84748
					
				
					 7 changed files with 476 additions and 133 deletions
				
			
		|  | @ -1,4 +1,5 @@ | ||||||
| #include "value-to-json.hh" | #include "value-to-json.hh" | ||||||
|  | #include "json.hh" | ||||||
| #include "eval-inline.hh" | #include "eval-inline.hh" | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
| 
 | 
 | ||||||
|  | @ -8,24 +9,8 @@ | ||||||
| 
 | 
 | ||||||
| namespace nix { | 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, | void printValueAsJSON(EvalState & state, bool strict, | ||||||
|     Value & v, std::ostream & str, PathSet & context) |     Value & v, JSONPlaceholder & out, PathSet & context) | ||||||
| { | { | ||||||
|     checkInterrupt(); |     checkInterrupt(); | ||||||
| 
 | 
 | ||||||
|  | @ -34,58 +19,58 @@ void printValueAsJSON(EvalState & state, bool strict, | ||||||
|     switch (v.type) { |     switch (v.type) { | ||||||
| 
 | 
 | ||||||
|         case tInt: |         case tInt: | ||||||
|             str << v.integer; |             out.write(v.integer); | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case tBool: |         case tBool: | ||||||
|             str << (v.boolean ? "true" : "false"); |             out.write(v.boolean); | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case tString: |         case tString: | ||||||
|             copyContext(v, context); |             copyContext(v, context); | ||||||
|             escapeJSON(str, v.string.s); |             out.write(v.string.s); | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case tPath: |         case tPath: | ||||||
|             escapeJSON(str, state.copyPathToStore(context, v.path)); |             out.write(state.copyPathToStore(context, v.path)); | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case tNull: |         case tNull: | ||||||
|             str << "null"; |             out.write(nullptr); | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case tAttrs: { |         case tAttrs: { | ||||||
|             Bindings::iterator i = v.attrs->find(state.sOutPath); |             Bindings::iterator i = v.attrs->find(state.sOutPath); | ||||||
|             if (i == v.attrs->end()) { |             if (i == v.attrs->end()) { | ||||||
|                 JSONObject json(str); |                 auto obj(out.object()); | ||||||
|                 StringSet names; |                 StringSet names; | ||||||
|                 for (auto & j : *v.attrs) |                 for (auto & j : *v.attrs) | ||||||
|                     names.insert(j.name); |                     names.insert(j.name); | ||||||
|                 for (auto & j : names) { |                 for (auto & j : names) { | ||||||
|                     Attr & a(*v.attrs->find(state.symbols.create(j))); |                     Attr & a(*v.attrs->find(state.symbols.create(j))); | ||||||
|                     json.attr(j); |                     auto placeholder(obj.placeholder(j)); | ||||||
|                     printValueAsJSON(state, strict, *a.value, str, context); |                     printValueAsJSON(state, strict, *a.value, placeholder, context); | ||||||
|                 } |                 } | ||||||
|             } else |             } else | ||||||
|                 printValueAsJSON(state, strict, *i->value, str, context); |                 printValueAsJSON(state, strict, *i->value, out, context); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         case tList1: case tList2: case tListN: { |         case tList1: case tList2: case tListN: { | ||||||
|             JSONList json(str); |             auto list(out.list()); | ||||||
|             for (unsigned int n = 0; n < v.listSize(); ++n) { |             for (unsigned int n = 0; n < v.listSize(); ++n) { | ||||||
|                 json.elem(); |                 auto placeholder(list.placeholder()); | ||||||
|                 printValueAsJSON(state, strict, *v.listElems()[n], str, context); |                 printValueAsJSON(state, strict, *v.listElems()[n], placeholder, context); | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         case tExternal: |         case tExternal: | ||||||
|             v.external->printValueAsJSON(state, strict, str, context); |             v.external->printValueAsJSON(state, strict, out, context); | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         case tFloat: |         case tFloat: | ||||||
|             str << v.fpoint; |             out.write(v.fpoint); | ||||||
|             break; |             break; | ||||||
| 
 | 
 | ||||||
|         default: |         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, | 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()); |     throw TypeError(format("cannot convert %1% to JSON") % showType()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,73 +8,12 @@ | ||||||
| 
 | 
 | ||||||
| namespace nix { | namespace nix { | ||||||
| 
 | 
 | ||||||
|  | class JSONPlaceholder; | ||||||
|  | 
 | ||||||
| void printValueAsJSON(EvalState & state, bool strict, | void printValueAsJSON(EvalState & state, bool strict, | ||||||
|     Value & v, std::ostream & out, PathSet & context); |     Value & v, JSONPlaceholder & out, PathSet & context); | ||||||
| 
 | 
 | ||||||
| void escapeJSON(std::ostream & str, const string & s); | void printValueAsJSON(EvalState & state, bool strict, | ||||||
| 
 |     Value & v, std::ostream & str, PathSet & context); | ||||||
| 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; |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| 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); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -36,6 +36,7 @@ class Symbol; | ||||||
| struct Pos; | struct Pos; | ||||||
| class EvalState; | class EvalState; | ||||||
| class XMLWriter; | class XMLWriter; | ||||||
|  | class JSONPlaceholder; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| typedef long NixInt; | typedef long NixInt; | ||||||
|  | @ -73,7 +74,7 @@ class ExternalValueBase | ||||||
| 
 | 
 | ||||||
|     /* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */ |     /* Print the value as JSON. Defaults to unconvertable, i.e. throws an error */ | ||||||
|     virtual void printValueAsJSON(EvalState & state, bool strict, |     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 */ |     /* Print the value as XML. Defaults to unevaluated */ | ||||||
|     virtual void printValueAsXML(EvalState & state, bool strict, bool location, |     virtual void printValueAsXML(EvalState & state, bool strict, bool location, | ||||||
|  |  | ||||||
							
								
								
									
										171
									
								
								src/libutil/json.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								src/libutil/json.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										183
									
								
								src/libutil/json.hh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								src/libutil/json.hh
									
										
									
									
									
										Normal file
									
								
							|  | @ -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(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include "store-api.hh" | #include "store-api.hh" | ||||||
| #include "user-env.hh" | #include "user-env.hh" | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
|  | #include "json.hh" | ||||||
| #include "value-to-json.hh" | #include "value-to-json.hh" | ||||||
| #include "xml-writer.hh" | #include "xml-writer.hh" | ||||||
| 
 | 
 | ||||||
|  | @ -860,26 +861,24 @@ static VersionDiff compareVersionAgainstSet( | ||||||
| 
 | 
 | ||||||
| static void queryJSON(Globals & globals, vector<DrvInfo> & elems) | static void queryJSON(Globals & globals, vector<DrvInfo> & elems) | ||||||
| { | { | ||||||
|     JSONObject topObj(cout); |     JSONObject topObj(cout, true); | ||||||
|     for (auto & i : elems) { |     for (auto & i : elems) { | ||||||
|         topObj.attr(i.attrPath); |         JSONObject pkgObj = topObj.object(i.attrPath); | ||||||
|         JSONObject pkgObj(cout); |  | ||||||
| 
 | 
 | ||||||
|         pkgObj.attr("name", i.name); |         pkgObj.attr("name", i.name); | ||||||
|         pkgObj.attr("system", i.system); |         pkgObj.attr("system", i.system); | ||||||
| 
 | 
 | ||||||
|         pkgObj.attr("meta"); |         JSONObject metaObj = pkgObj.object("meta"); | ||||||
|         JSONObject metaObj(cout); |  | ||||||
|         StringSet metaNames = i.queryMetaNames(); |         StringSet metaNames = i.queryMetaNames(); | ||||||
|         for (auto & j : metaNames) { |         for (auto & j : metaNames) { | ||||||
|             metaObj.attr(j); |             auto placeholder = metaObj.placeholder(j); | ||||||
|             Value * v = i.queryMeta(j); |             Value * v = i.queryMeta(j); | ||||||
|             if (!v) { |             if (!v) { | ||||||
|                 printMsg(lvlError, format("derivation ‘%1%’ has invalid meta attribute ‘%2%’") % i.name % j); |                 printMsg(lvlError, format("derivation ‘%1%’ has invalid meta attribute ‘%2%’") % i.name % j); | ||||||
|                 cout << "null"; |                 placeholder.write(nullptr); | ||||||
|             } else { |             } else { | ||||||
|                 PathSet context; |                 PathSet context; | ||||||
|                 printValueAsJSON(*globals.state, true, *v, cout, context); |                 printValueAsJSON(*globals.state, true, *v, placeholder, context); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -2,6 +2,10 @@ | ||||||
| #include "shared.hh" | #include "shared.hh" | ||||||
| #include "store-api.hh" | #include "store-api.hh" | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | #include "json.hh" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| #include <iomanip> | #include <iomanip> | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| 
 | 
 | ||||||
|  | @ -12,12 +16,14 @@ struct CmdPathInfo : StorePathsCommand | ||||||
|     bool showSize = false; |     bool showSize = false; | ||||||
|     bool showClosureSize = false; |     bool showClosureSize = false; | ||||||
|     bool showSigs = false; |     bool showSigs = false; | ||||||
|  |     bool json = false; | ||||||
| 
 | 
 | ||||||
|     CmdPathInfo() |     CmdPathInfo() | ||||||
|     { |     { | ||||||
|         mkFlag('s', "size", "print size of the NAR dump of each path", &showSize); |         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('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, "sigs", "show signatures", &showSigs); | ||||||
|  |         mkFlag(0, "json", "produce JSON output", &json); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string name() override |     std::string name() override | ||||||
|  | @ -41,6 +47,10 @@ struct CmdPathInfo : StorePathsCommand | ||||||
|                 "To check the existence of a path in a binary cache:", |                 "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/" |                 "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) |         for (auto & storePath : storePaths) | ||||||
|             pathLen = std::max(pathLen, storePath.size()); |             pathLen = std::max(pathLen, storePath.size()); | ||||||
| 
 | 
 | ||||||
|         for (auto storePath : storePaths) { |         auto getClosureSize = [&](const Path & storePath) { | ||||||
|             auto info = store->queryPathInfo(storePath); |             size_t totalSize = 0; | ||||||
|             storePath = info->path; // FIXME: screws up padding
 |             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) { |             for (auto storePath : storePaths) { | ||||||
|                 std::cout << '\t' << std::setw(11) << info->narSize; |                 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 (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); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|             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 (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; |  | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         else { | ||||||
|  | 
 | ||||||
|  |             for (auto storePath : storePaths) { | ||||||
|  |                 auto info = store->queryPathInfo(storePath); | ||||||
|  |                 storePath = info->path; // FIXME: screws up padding
 | ||||||
|  | 
 | ||||||
|  |                 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; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue