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" {...@@ -2241,7 +2241,45 @@ test "string copy option" {
2241}2241}
22422242
2243pub const StringifyOptions = struct {2243pub 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
2245 // TODO: make escaping '/' in strings optional?2283 // TODO: make escaping '/' in strings optional?
2246 // TODO: allow picking if []u8 is string or array?2284 // TODO: allow picking if []u8 is string or array?
2247};2285};
...@@ -2297,8 +2335,12 @@ pub fn stringify(...@@ -2297,8 +2335,12 @@ pub fn stringify(
2297 return value.jsonStringify(options, out_stream);2335 return value.jsonStringify(options, out_stream);
2298 }2336 }
22992337
2300 try out_stream.writeAll("{");2338 try out_stream.writeByte('{');
2301 comptime var field_output = false;2339 comptime var field_output = false;
2340 var child_options = options;
2341 if (child_options.whitespace) |*child_whitespace| {
2342 child_whitespace.indent_level += 1;
2343 }
2302 inline for (S.fields) |Field, field_i| {2344 inline for (S.fields) |Field, field_i| {
2303 // don't include void fields2345 // don't include void fields
2304 if (Field.field_type == void) continue;2346 if (Field.field_type == void) continue;
...@@ -2306,14 +2348,28 @@ pub fn stringify(...@@ -2306,14 +2348,28 @@ pub fn stringify(
2306 if (!field_output) {2348 if (!field_output) {
2307 field_output = true;2349 field_output = true;
2308 } else {2350 } 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);
2310 }2356 }
2311
2312 try stringify(Field.name, options, out_stream);2357 try stringify(Field.name, options, out_stream);
2313 try out_stream.writeAll(":");2358 try out_stream.writeByte(':');
2314 try stringify(@field(value, Field.name), options, out_stream);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);
2315 }2365 }
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('}');
2317 return;2373 return;
2318 },2374 },
2319 .Pointer => |ptr_info| switch (ptr_info.size) {2375 .Pointer => |ptr_info| switch (ptr_info.size) {
...@@ -2330,7 +2386,7 @@ pub fn stringify(...@@ -2330,7 +2386,7 @@ pub fn stringify(
2330 // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972)2386 // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972)
2331 .Slice => {2387 .Slice => {
2332 if (ptr_info.child == u8 and std.unicode.utf8ValidateSlice(value)) {2388 if (ptr_info.child == u8 and std.unicode.utf8ValidateSlice(value)) {
2333 try out_stream.writeAll("\"");2389 try out_stream.writeByte('\"');
2334 var i: usize = 0;2390 var i: usize = 0;
2335 while (i < value.len) : (i += 1) {2391 while (i < value.len) : (i += 1) {
2336 switch (value[i]) {2392 switch (value[i]) {
...@@ -2368,18 +2424,32 @@ pub fn stringify(...@@ -2368,18 +2424,32 @@ pub fn stringify(
2368 },2424 },
2369 }2425 }
2370 }2426 }
2371 try out_stream.writeAll("\"");2427 try out_stream.writeByte('\"');
2372 return;2428 return;
2373 }2429 }
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 }
2376 for (value) |x, i| {2436 for (value) |x, i| {
2377 if (i != 0) {2437 if (i != 0) {
2378 try out_stream.writeAll(",");2438 try out_stream.writeByte(',');
2379 }2439 }
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);
2381 }2445 }
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(']');
2383 return;2453 return;
2384 },2454 },
2385 else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),2455 else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
...@@ -2486,6 +2556,46 @@ test "stringify struct" {...@@ -2486,6 +2556,46 @@ test "stringify struct" {
2486 }{ .foo = 42 }, StringifyOptions{});2556 }{ .foo = 42 }, StringifyOptions{});
2487}2557}
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
2489test "stringify struct with void field" {2599test "stringify struct with void field" {
2490 try teststringify("{\"foo\":42}", struct {2600 try teststringify("{\"foo\":42}", struct {
2491 foo: u32,2601 foo: u32,
...@@ -2515,7 +2625,7 @@ test "stringify struct with custom stringifier" {...@@ -2515,7 +2625,7 @@ test "stringify struct with custom stringifier" {
2515 ) !void {2625 ) !void {
2516 try out_stream.writeAll("[\"something special\",");2626 try out_stream.writeAll("[\"something special\",");
2517 try stringify(42, options, out_stream);2627 try stringify(42, options, out_stream);
2518 try out_stream.writeAll("]");2628 try out_stream.writeByte(']');
2519 }2629 }
2520 }{ .foo = 42 }, StringifyOptions{});2630 }{ .foo = 42 }, StringifyOptions{});
2521}2631}