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…
	
	Add table
		Add a link
		
	
		Reference in a new issue