| ... | ... | @@ -2963,6 +2963,47 @@ fn outputUnicodeEscape( |
| 2963 | 2963 | } |
| 2964 | 2964 | } |
| 2965 | 2965 | |
| 2966 | fn outputJsonString(value: []const u8, options: StringifyOptions, out_stream: anytype) !void { |
| 2967 | try out_stream.writeByte('\"'); |
| 2968 | var i: usize = 0; |
| 2969 | while (i < value.len) : (i += 1) { |
| 2970 | switch (value[i]) { |
| 2971 | // normal ascii character |
| 2972 | 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => |c| try out_stream.writeByte(c), |
| 2973 | // only 2 characters that *must* be escaped |
| 2974 | '\\' => try out_stream.writeAll("\\\\"), |
| 2975 | '\"' => try out_stream.writeAll("\\\""), |
| 2976 | // solidus is optional to escape |
| 2977 | '/' => { |
| 2978 | if (options.string.String.escape_solidus) { |
| 2979 | try out_stream.writeAll("\\/"); |
| 2980 | } else { |
| 2981 | try out_stream.writeByte('/'); |
| 2982 | } |
| 2983 | }, |
| 2984 | // control characters with short escapes |
| 2985 | // TODO: option to switch between unicode and 'short' forms? |
| 2986 | 0x8 => try out_stream.writeAll("\\b"), |
| 2987 | 0xC => try out_stream.writeAll("\\f"), |
| 2988 | '\n' => try out_stream.writeAll("\\n"), |
| 2989 | '\r' => try out_stream.writeAll("\\r"), |
| 2990 | '\t' => try out_stream.writeAll("\\t"), |
| 2991 | else => { |
| 2992 | const ulen = std.unicode.utf8ByteSequenceLength(value[i]) catch unreachable; |
| 2993 | // control characters (only things left with 1 byte length) should always be printed as unicode escapes |
| 2994 | if (ulen == 1 or options.string.String.escape_unicode) { |
| 2995 | const codepoint = std.unicode.utf8Decode(value[i .. i + ulen]) catch unreachable; |
| 2996 | try outputUnicodeEscape(codepoint, out_stream); |
| 2997 | } else { |
| 2998 | try out_stream.writeAll(value[i .. i + ulen]); |
| 2999 | } |
| 3000 | i += ulen - 1; |
| 3001 | }, |
| 3002 | } |
| 3003 | } |
| 3004 | try out_stream.writeByte('\"'); |
| 3005 | } |
| 3006 | |
| 2966 | 3007 | pub fn stringify( |
| 2967 | 3008 | value: anytype, |
| 2968 | 3009 | options: StringifyOptions, |
| ... | ... | @@ -3048,7 +3089,7 @@ pub fn stringify( |
| 3048 | 3089 | try out_stream.writeByte('\n'); |
| 3049 | 3090 | try child_whitespace.outputIndent(out_stream); |
| 3050 | 3091 | } |
| 3051 | | try stringify(Field.name, options, out_stream); |
| 3092 | try outputJsonString(Field.name, options, out_stream); |
| 3052 | 3093 | try out_stream.writeByte(':'); |
| 3053 | 3094 | if (child_options.whitespace) |child_whitespace| { |
| 3054 | 3095 | if (child_whitespace.separator) { |
| ... | ... | @@ -3082,44 +3123,7 @@ pub fn stringify( |
| 3082 | 3123 | // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972) |
| 3083 | 3124 | .Slice => { |
| 3084 | 3125 | if (ptr_info.child == u8 and options.string == .String and std.unicode.utf8ValidateSlice(value)) { |
| 3085 | | try out_stream.writeByte('\"'); |
| 3086 | | var i: usize = 0; |
| 3087 | | while (i < value.len) : (i += 1) { |
| 3088 | | switch (value[i]) { |
| 3089 | | // normal ascii character |
| 3090 | | 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => |c| try out_stream.writeByte(c), |
| 3091 | | // only 2 characters that *must* be escaped |
| 3092 | | '\\' => try out_stream.writeAll("\\\\"), |
| 3093 | | '\"' => try out_stream.writeAll("\\\""), |
| 3094 | | // solidus is optional to escape |
| 3095 | | '/' => { |
| 3096 | | if (options.string.String.escape_solidus) { |
| 3097 | | try out_stream.writeAll("\\/"); |
| 3098 | | } else { |
| 3099 | | try out_stream.writeByte('/'); |
| 3100 | | } |
| 3101 | | }, |
| 3102 | | // control characters with short escapes |
| 3103 | | // TODO: option to switch between unicode and 'short' forms? |
| 3104 | | 0x8 => try out_stream.writeAll("\\b"), |
| 3105 | | 0xC => try out_stream.writeAll("\\f"), |
| 3106 | | '\n' => try out_stream.writeAll("\\n"), |
| 3107 | | '\r' => try out_stream.writeAll("\\r"), |
| 3108 | | '\t' => try out_stream.writeAll("\\t"), |
| 3109 | | else => { |
| 3110 | | const ulen = std.unicode.utf8ByteSequenceLength(value[i]) catch unreachable; |
| 3111 | | // control characters (only things left with 1 byte length) should always be printed as unicode escapes |
| 3112 | | if (ulen == 1 or options.string.String.escape_unicode) { |
| 3113 | | const codepoint = std.unicode.utf8Decode(value[i .. i + ulen]) catch unreachable; |
| 3114 | | try outputUnicodeEscape(codepoint, out_stream); |
| 3115 | | } else { |
| 3116 | | try out_stream.writeAll(value[i .. i + ulen]); |
| 3117 | | } |
| 3118 | | i += ulen - 1; |
| 3119 | | }, |
| 3120 | | } |
| 3121 | | } |
| 3122 | | try out_stream.writeByte('\"'); |
| 3126 | try outputJsonString(value, options, out_stream); |
| 3123 | 3127 | return; |
| 3124 | 3128 | } |
| 3125 | 3129 | |
| ... | ... | @@ -3268,6 +3272,11 @@ test "stringify struct" { |
| 3268 | 3272 | }{ .foo = 42 }, StringifyOptions{}); |
| 3269 | 3273 | } |
| 3270 | 3274 | |
| 3275 | test "stringify struct with string as array" { |
| 3276 | try teststringify("{\"foo\":\"bar\"}", .{ .foo = "bar" }, StringifyOptions{}); |
| 3277 | try teststringify("{\"foo\":[98,97,114]}", .{ .foo = "bar" }, StringifyOptions{ .string = .Array }); |
| 3278 | } |
| 3279 | |
| 3271 | 3280 | test "stringify struct with indentation" { |
| 3272 | 3281 | try teststringify( |
| 3273 | 3282 | \\{ |