Skip to content

Commit 4162f40

Browse files
authored
std.fmt: Add unit tests for all methods in the Parser struct
1 parent 8d914ea commit 4162f40

File tree

1 file changed

+244
-5
lines changed

1 file changed

+244
-5
lines changed

lib/std/fmt.zig

Lines changed: 244 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const unicode = std.unicode;
1111
const meta = std.meta;
1212
const lossyCast = math.lossyCast;
1313
const expectFmt = std.testing.expectFmt;
14+
const testing = std.testing;
1415

1516
pub const default_max_depth = 3;
1617

@@ -319,7 +320,6 @@ pub const Specifier = union(enum) {
319320
/// Allows to implement formatters compatible with std.fmt without replicating
320321
/// the standard library behavior.
321322
pub const Parser = struct {
322-
pos: usize = 0,
323323
iter: std.unicode.Utf8Iterator,
324324

325325
// Returns a decimal number or null if the current character is not a
@@ -345,23 +345,26 @@ pub const Parser = struct {
345345
// Returns a substring of the input starting from the current position
346346
// and ending where `ch` is found or until the end if not found
347347
pub fn until(self: *@This(), ch: u21) []const u8 {
348-
var result: []const u8 = &[_]u8{};
348+
const start = self.iter.i;
349349
while (self.peek(0)) |code_point| {
350350
if (code_point == ch)
351351
break;
352-
result = result ++ (self.iter.nextCodepointSlice() orelse &[_]u8{});
352+
_ = self.iter.nextCodepoint();
353353
}
354-
return result;
354+
return self.iter.bytes[start..self.iter.i];
355355
}
356356

357-
// Returns one character, if available
357+
// Returns the character pointed to by the iterator if available, or
358+
// null otherwise
358359
pub fn char(self: *@This()) ?u21 {
359360
if (self.iter.nextCodepoint()) |code_point| {
360361
return code_point;
361362
}
362363
return null;
363364
}
364365

366+
// Returns true if the iterator points to an existing character and
367+
// false otherwise
365368
pub fn maybe(self: *@This(), val: u21) bool {
366369
if (self.peek(0) == val) {
367370
_ = self.iter.nextCodepoint();
@@ -2779,3 +2782,239 @@ test hex {
27792782
try std.testing.expectEqualStrings("[00efcdab78563412]", s);
27802783
}
27812784
}
2785+
2786+
test "parser until" {
2787+
{ // return substring till ':'
2788+
var parser: Parser = .{
2789+
.iter = .{ .bytes = "abc:1234", .i = 0 },
2790+
};
2791+
try testing.expectEqualStrings("abc", parser.until(':'));
2792+
}
2793+
2794+
{ // return the entire string - `ch` not found
2795+
var parser: Parser = .{
2796+
.iter = .{ .bytes = "abc1234", .i = 0 },
2797+
};
2798+
try testing.expectEqualStrings("abc1234", parser.until(':'));
2799+
}
2800+
2801+
{ // substring is empty - `ch` is the only character
2802+
var parser: Parser = .{
2803+
.iter = .{ .bytes = ":", .i = 0 },
2804+
};
2805+
try testing.expectEqualStrings("", parser.until(':'));
2806+
}
2807+
2808+
{ // empty string and `ch` not found
2809+
var parser: Parser = .{
2810+
.iter = .{ .bytes = "", .i = 0 },
2811+
};
2812+
try testing.expectEqualStrings("", parser.until(':'));
2813+
}
2814+
2815+
{ // substring starts at index 2 and goes upto `ch`
2816+
var parser: Parser = .{
2817+
.iter = .{ .bytes = "abc:1234", .i = 2 },
2818+
};
2819+
try testing.expectEqualStrings("c", parser.until(':'));
2820+
}
2821+
2822+
{ // substring starts at index 4 and goes upto the end - `ch` not found
2823+
var parser: Parser = .{
2824+
.iter = .{ .bytes = "abc1234", .i = 4 },
2825+
};
2826+
try testing.expectEqualStrings("234", parser.until(':'));
2827+
}
2828+
}
2829+
2830+
test "parser peek" {
2831+
{ // start iteration from the first index
2832+
var parser: Parser = .{
2833+
.iter = .{ .bytes = "hello world", .i = 0 },
2834+
};
2835+
2836+
try testing.expectEqual('h', parser.peek(0));
2837+
try testing.expectEqual('e', parser.peek(1));
2838+
try testing.expectEqual(' ', parser.peek(5));
2839+
try testing.expectEqual('d', parser.peek(10));
2840+
try testing.expectEqual(null, parser.peek(11));
2841+
}
2842+
2843+
{ // start iteration from the second last index
2844+
var parser: Parser = .{
2845+
.iter = .{ .bytes = "hello world!", .i = 10 },
2846+
};
2847+
2848+
try testing.expectEqual('d', parser.peek(0));
2849+
try testing.expectEqual('!', parser.peek(1));
2850+
try testing.expectEqual(null, parser.peek(5));
2851+
}
2852+
2853+
{ // start iteration beyond the length of the string
2854+
var parser: Parser = .{
2855+
.iter = .{ .bytes = "hello", .i = 5 },
2856+
};
2857+
2858+
try testing.expectEqual(null, parser.peek(0));
2859+
try testing.expectEqual(null, parser.peek(1));
2860+
}
2861+
2862+
{ // empty string
2863+
var parser: Parser = .{
2864+
.iter = .{ .bytes = "", .i = 0 },
2865+
};
2866+
2867+
try testing.expectEqual(null, parser.peek(0));
2868+
try testing.expectEqual(null, parser.peek(2));
2869+
}
2870+
}
2871+
2872+
test "parser char" {
2873+
// character exists - iterator at 0
2874+
var parser: Parser = .{ .iter = .{ .bytes = "~~hello", .i = 0 } };
2875+
try testing.expectEqual('~', parser.char());
2876+
2877+
// character exists - iterator in the middle
2878+
parser = .{ .iter = .{ .bytes = "~~hello", .i = 3 } };
2879+
try testing.expectEqual('e', parser.char());
2880+
2881+
// character exists - iterator at the end
2882+
parser = .{ .iter = .{ .bytes = "~~hello", .i = 6 } };
2883+
try testing.expectEqual('o', parser.char());
2884+
2885+
// character doesn't exist - iterator beyond the length of the string
2886+
parser = .{ .iter = .{ .bytes = "~~hello", .i = 7 } };
2887+
try testing.expectEqual(null, parser.char());
2888+
}
2889+
2890+
test "parser maybe" {
2891+
// character exists - iterator at 0
2892+
var parser: Parser = .{ .iter = .{ .bytes = "hello world", .i = 0 } };
2893+
try testing.expect(parser.maybe('h'));
2894+
2895+
// character exists - iterator at space
2896+
parser = .{ .iter = .{ .bytes = "hello world", .i = 5 } };
2897+
try testing.expect(parser.maybe(' '));
2898+
2899+
// character exists - iterator at the end
2900+
parser = .{ .iter = .{ .bytes = "hello world", .i = 10 } };
2901+
try testing.expect(parser.maybe('d'));
2902+
2903+
// character doesn't exist - iterator beyond the length of the string
2904+
parser = .{ .iter = .{ .bytes = "hello world", .i = 11 } };
2905+
try testing.expect(!parser.maybe('e'));
2906+
}
2907+
2908+
test "parser number" {
2909+
// input is a single digit natural number - iterator at 0
2910+
var parser: Parser = .{ .iter = .{ .bytes = "7", .i = 0 } };
2911+
try testing.expect(7 == parser.number());
2912+
2913+
// input is a two digit natural number - iterator at 1
2914+
parser = .{ .iter = .{ .bytes = "29", .i = 1 } };
2915+
try testing.expect(9 == parser.number());
2916+
2917+
// input is a two digit natural number - iterator beyond the length of the string
2918+
parser = .{ .iter = .{ .bytes = "32", .i = 2 } };
2919+
try testing.expectEqual(null, parser.number());
2920+
2921+
// input is an integer
2922+
parser = .{ .iter = .{ .bytes = "0", .i = 0 } };
2923+
try testing.expect(0 == parser.number());
2924+
2925+
// input is a negative integer
2926+
parser = .{ .iter = .{ .bytes = "-2", .i = 0 } };
2927+
try testing.expectEqual(null, parser.number());
2928+
2929+
// input is a string
2930+
parser = .{ .iter = .{ .bytes = "no_number", .i = 2 } };
2931+
try testing.expectEqual(null, parser.number());
2932+
2933+
// input is a single character string
2934+
parser = .{ .iter = .{ .bytes = "n", .i = 0 } };
2935+
try testing.expectEqual(null, parser.number());
2936+
2937+
// input is an empty string
2938+
parser = .{ .iter = .{ .bytes = "", .i = 0 } };
2939+
try testing.expectEqual(null, parser.number());
2940+
}
2941+
2942+
test "parser specifier" {
2943+
{ // input string is a digit; iterator at 0
2944+
const expected: Specifier = Specifier{ .number = 1 };
2945+
var parser: Parser = .{ .iter = .{ .bytes = "1", .i = 0 } };
2946+
2947+
const result = try parser.specifier();
2948+
try testing.expect(expected.number == result.number);
2949+
}
2950+
2951+
{ // input string is a two digit number; iterator at 0
2952+
const digit: Specifier = Specifier{ .number = 42 };
2953+
var parser: Parser = .{ .iter = .{ .bytes = "42", .i = 0 } };
2954+
2955+
const result = try parser.specifier();
2956+
try testing.expect(digit.number == result.number);
2957+
}
2958+
2959+
{ // input string is a two digit number digit; iterator at 1
2960+
const digit: Specifier = Specifier{ .number = 8 };
2961+
var parser: Parser = .{ .iter = .{ .bytes = "28", .i = 1 } };
2962+
2963+
const result = try parser.specifier();
2964+
try testing.expect(digit.number == result.number);
2965+
}
2966+
2967+
{ // input string is a two digit number with square brackets; iterator at 0
2968+
const digit: Specifier = Specifier{ .named = "15" };
2969+
var parser: Parser = .{ .iter = .{ .bytes = "[15]", .i = 0 } };
2970+
2971+
const result = try parser.specifier();
2972+
try testing.expectEqualStrings(digit.named, result.named);
2973+
}
2974+
2975+
{ // input string is not a number and contains square brackets; iterator at 0
2976+
const digit: Specifier = Specifier{ .named = "hello" };
2977+
var parser: Parser = .{ .iter = .{ .bytes = "[hello]", .i = 0 } };
2978+
2979+
const result = try parser.specifier();
2980+
try testing.expectEqualStrings(digit.named, result.named);
2981+
}
2982+
2983+
{ // input string is not a number and doesn't contain closing square bracket; iterator at 0
2984+
var parser: Parser = .{ .iter = .{ .bytes = "[hello", .i = 0 } };
2985+
2986+
const result = parser.specifier();
2987+
try testing.expectError(@field(anyerror, "Expected closing ]"), result);
2988+
}
2989+
2990+
{ // input string is not a number and doesn't contain closing square bracket; iterator at 2
2991+
var parser: Parser = .{ .iter = .{ .bytes = "[[[[hello", .i = 2 } };
2992+
2993+
const result = parser.specifier();
2994+
try testing.expectError(@field(anyerror, "Expected closing ]"), result);
2995+
}
2996+
2997+
{ // input string is not a number and contains unbalanced square brackets; iterator at 0
2998+
const digit: Specifier = Specifier{ .named = "[[hello" };
2999+
var parser: Parser = .{ .iter = .{ .bytes = "[[[hello]", .i = 0 } };
3000+
3001+
const result = try parser.specifier();
3002+
try testing.expectEqualStrings(digit.named, result.named);
3003+
}
3004+
3005+
{ // input string is not a number and contains unbalanced square brackets; iterator at 1
3006+
const digit: Specifier = Specifier{ .named = "[[hello" };
3007+
var parser: Parser = .{ .iter = .{ .bytes = "[[[[hello]]]]]", .i = 1 } };
3008+
3009+
const result = try parser.specifier();
3010+
try testing.expectEqualStrings(digit.named, result.named);
3011+
}
3012+
3013+
{ // input string is neither a digit nor a named argument
3014+
const char: Specifier = Specifier{ .none = {} };
3015+
var parser: Parser = .{ .iter = .{ .bytes = "hello", .i = 0 } };
3016+
3017+
const result = try parser.specifier();
3018+
try testing.expectEqual(char.none, result.none);
3019+
}
3020+
}

0 commit comments

Comments
 (0)