authorgravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2020-02-25 00:53:14+11:00
committergravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2020-03-31 23:57:00+11:00
log48e7c6cca67bcd9f41c2742c83353b03c52f9b50
tree890912390ac3f352ec4a65d8f5bb7f0cad9d14b1
parent3a0875d9e805e524a12756ff975fedb0d94fb217
signature Commit is signed but in an unrecognized format.

std: add whitespace control to json.stringify


1 files changed, 124 insertions(+), 14 deletions(-)

lib/std/json.zig+124-14
......@@ -2241,7 +2241,45 @@ test "string copy option" {
22412241}
22422242
22432243pub const StringifyOptions = struct {
2244 // TODO: indentation options?
2244 pub const Whitespace = struct {
2245 /// How many indentation levels deep are we?
2246 indent_level: usize = 0,
2247
2248 pub const Indentation = union(enum) {
2249 Space: u8,
2250 Tab: void,
2251 };
2252
2253 /// What character(s) should be used for indentation?
2254 indent: Indentation = Indentation{ .Space = 4 },
2255
2256 fn outputIndent(
2257 whitespace: @This(),
2258 out_stream: var,
2259 ) @TypeOf(out_stream).Error!void {
2260 var char: u8 = undefined;
2261 var n_chars: usize = undefined;
2262 switch (whitespace.indent) {
2263 .Space => |n_spaces| {
2264 char = ' ';
2265 n_chars = n_spaces;
2266 },
2267 .Tab => {
2268 char = '\t';
2269 n_chars = 1;
2270 },
2271 }
2272 n_chars *= whitespace.indent_level;
2273 try out_stream.writeByteNTimes(char, n_chars);
2274 }
2275
2276 /// After a colon, should whitespace be inserted?
2277 separator: bool = true,
2278 };
2279
2280 /// Controls the whitespace emitted
2281 whitespace: ?Whitespace = null,
2282
22452283 // TODO: make escaping '/' in strings optional?
22462284 // TODO: allow picking if []u8 is string or array?
22472285};
......@@ -2297,8 +2335,12 @@ pub fn stringify(
22972335 return value.jsonStringify(options, out_stream);
22982336 }
22992337
2300 try out_stream.writeAll("{");
2338 try out_stream.writeByte('{');
23012339 comptime var field_output = false;
2340 var child_options = options;
2341 if (child_options.whitespace) |*child_whitespace| {
2342 child_whitespace.indent_level += 1;
2343 }
23022344 inline for (S.fields) |Field, field_i| {
23032345 // don't include void fields
23042346 if (Field.field_type == void) continue;
......@@ -2306,14 +2348,28 @@ pub fn stringify(
23062348 if (!field_output) {
23072349 field_output = true;
23082350 } else {
2309 try out_stream.writeAll(",");
2351 try out_stream.writeByte(',');
2352 }
2353 if (child_options.whitespace) |child_whitespace| {
2354 try out_stream.writeByte('\n');
2355 try child_whitespace.outputIndent(out_stream);
23102356 }
2311
23122357 try stringify(Field.name, options, out_stream);
2313 try out_stream.writeAll(":");
2314 try stringify(@field(value, Field.name), options, out_stream);
2358 try out_stream.writeByte(':');
2359 if (child_options.whitespace) |child_whitespace| {
2360 if (child_whitespace.separator) {
2361 try out_stream.writeByte(' ');
2362 }
2363 }
2364 try stringify(@field(value, Field.name), child_options, out_stream);
23152365 }
2316 try out_stream.writeAll("}");
2366 if (field_output) {
2367 if (options.whitespace) |whitespace| {
2368 try out_stream.writeByte('\n');
2369 try whitespace.outputIndent(out_stream);
2370 }
2371 }
2372 try out_stream.writeByte('}');
23172373 return;
23182374 },
23192375 .Pointer => |ptr_info| switch (ptr_info.size) {
......@@ -2330,7 +2386,7 @@ pub fn stringify(
23302386 // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972)
23312387 .Slice => {
23322388 if (ptr_info.child == u8 and std.unicode.utf8ValidateSlice(value)) {
2333 try out_stream.writeAll("\"");
2389 try out_stream.writeByte('\"');
23342390 var i: usize = 0;
23352391 while (i < value.len) : (i += 1) {
23362392 switch (value[i]) {
......@@ -2368,18 +2424,32 @@ pub fn stringify(
23682424 },
23692425 }
23702426 }
2371 try out_stream.writeAll("\"");
2427 try out_stream.writeByte('\"');
23722428 return;
23732429 }
23742430
2375 try out_stream.writeAll("[");
2431 try out_stream.writeByte('[');
2432 var child_options = options;
2433 if (child_options.whitespace) |*whitespace| {
2434 whitespace.indent_level += 1;
2435 }
23762436 for (value) |x, i| {
23772437 if (i != 0) {
2378 try out_stream.writeAll(",");
2438 try out_stream.writeByte(',');
23792439 }
2380 try stringify(x, options, out_stream);
2440 if (child_options.whitespace) |child_whitespace| {
2441 try out_stream.writeByte('\n');
2442 try child_whitespace.outputIndent(out_stream);
2443 }
2444 try stringify(x, child_options, out_stream);
23812445 }
2382 try out_stream.writeAll("]");
2446 if (value.len != 0) {
2447 if (options.whitespace) |whitespace| {
2448 try out_stream.writeByte('\n');
2449 try whitespace.outputIndent(out_stream);
2450 }
2451 }
2452 try out_stream.writeByte(']');
23832453 return;
23842454 },
23852455 else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
......@@ -2486,6 +2556,46 @@ test "stringify struct" {
24862556 }{ .foo = 42 }, StringifyOptions{});
24872557}
24882558
2559test "stringify struct with indentation" {
2560 try teststringify(
2561 \\{
2562 \\ "foo": 42,
2563 \\ "bar": [
2564 \\ 1,
2565 \\ 2,
2566 \\ 3
2567 \\ ]
2568 \\}
2569 ,
2570 struct {
2571 foo: u32,
2572 bar: [3]u32,
2573 }{
2574 .foo = 42,
2575 .bar = .{ 1, 2, 3 },
2576 },
2577 StringifyOptions{
2578 .whitespace = .{},
2579 },
2580 );
2581 try teststringify(
2582 "{\n\t\"foo\":42,\n\t\"bar\":[\n\t\t1,\n\t\t2,\n\t\t3\n\t]\n}",
2583 struct {
2584 foo: u32,
2585 bar: [3]u32,
2586 }{
2587 .foo = 42,
2588 .bar = .{ 1, 2, 3 },
2589 },
2590 StringifyOptions{
2591 .whitespace = .{
2592 .indent = .Tab,
2593 .separator = false,
2594 },
2595 },
2596 );
2597}
2598
24892599test "stringify struct with void field" {
24902600 try teststringify("{\"foo\":42}", struct {
24912601 foo: u32,
......@@ -2515,7 +2625,7 @@ test "stringify struct with custom stringifier" {
25152625 ) !void {
25162626 try out_stream.writeAll("[\"something special\",");
25172627 try stringify(42, options, out_stream);
2518 try out_stream.writeAll("]");
2628 try out_stream.writeByte(']');
25192629 }
25202630 }{ .foo = 42 }, StringifyOptions{});
25212631}