@@ -881,31 +881,82 @@ pub const Walker = struct {
881881 }
882882};
883883
884- // write a JSON string.
885- // replaces all whitspaces with a single space.
884+ // Replace successives whitespaces with one withespace.
885+ // Trims left and right according to the options.
886+ // Returns true if the string ends with a trimmed whitespace.
886887fn writeString (s : []const u8 , w : anytype ) ! void {
887- if (! std .unicode .utf8ValidateSlice (s )) {
888- return error .InvalidUTF8String ;
889- }
890-
891- // replace white spaces with single space.
892888 try w .beginWriteRaw ();
893889 try w .writer .writeByte ('\" ' );
894- var cursor : usize = 0 ;
890+ try stripWhitespaces (s , w .writer );
891+ try w .writer .writeByte ('\" ' );
892+ w .endWriteRaw ();
893+ }
894+
895+ fn stripWhitespaces (s : []const u8 , writer : anytype ) ! void {
896+ var start : usize = 0 ;
897+ var prev_w : ? bool = null ;
898+ var is_w : bool = undefined ;
899+
895900 for (s , 0.. ) | c , i | {
896- if (std .ascii .isWhitespace (c )) {
897- // write string until space
898- if (cursor < i ) {
899- try w .writer .writeAll (s [cursor .. i ]);
900- try w .writer .writeByte (' ' );
901+ is_w = std .ascii .isWhitespace (c );
902+
903+ // Detect the first char type.
904+ if (prev_w == null ) {
905+ prev_w = is_w ;
906+ }
907+ // The current char is the same kind of char, the chunk continues.
908+ if (prev_w .? == is_w ) {
909+ continue ;
910+ }
911+
912+ // Starting here, the chunk changed.
913+ if (is_w ) {
914+ // We have a chunk of non-whitespaces, we write it as it.
915+ try writer .writeAll (s [start .. i ]);
916+ } else {
917+ // We have a chunk of whitespaces, replace with one space,
918+ // depending the position.
919+ if (start > 0 ) {
920+ try writer .writeByte (' ' );
901921 }
902- cursor = i + 1 ;
903922 }
923+ // Start the new chunk.
924+ prev_w = is_w ;
925+ start = i ;
904926 }
905- // write the reminder string
906- if (cursor < s .len ) {
907- try w .writer .writeAll (s [cursor .. ]);
927+ // Write the reminder chunk.
928+ if (! is_w ) {
929+ // last chunk is non whitespaces.
930+ try writer .writeAll (s [start .. ]);
931+ }
932+ }
933+
934+ test "AXnode: stripWhitespaces" {
935+ const allocator = std .testing .allocator ;
936+
937+ const TestCase = struct {
938+ value : []const u8 ,
939+ expected : []const u8 ,
940+ };
941+
942+ const test_cases = [_ ]TestCase {
943+ .{ .value = " " , .expected = "" },
944+ .{ .value = " " , .expected = "" },
945+ .{ .value = "foo bar" , .expected = "foo bar" },
946+ .{ .value = "foo bar" , .expected = "foo bar" },
947+ .{ .value = " foo bar" , .expected = "foo bar" },
948+ .{ .value = "foo bar " , .expected = "foo bar" },
949+ .{ .value = " foo bar " , .expected = "foo bar" },
950+ .{ .value = "foo\n \t bar" , .expected = "foo bar" },
951+ .{ .value = "\t foo bar baz \t \n yeah\r \n " , .expected = "foo bar baz yeah" },
952+ };
953+
954+ var buffer = std .io .Writer .Allocating .init (allocator );
955+ defer buffer .deinit ();
956+
957+ for (test_cases ) | test_case | {
958+ buffer .clearRetainingCapacity ();
959+ try stripWhitespaces (test_case .value , & buffer .writer );
960+ try std .testing .expectEqualStrings (test_case .expected , buffer .written ());
908961 }
909- try w .writer .writeByte ('\" ' );
910- w .endWriteRaw ();
911962}
0 commit comments