| ... | ... | @@ -13,7 +13,13 @@ pub const default_max_depth = 3; |
| 13 | 13 | /// Renders fmt string with args, calling output with slices of bytes. |
| 14 | 14 | /// If `output` returns an error, the error is returned from `format` and |
| 15 | 15 | /// `output` is not called again. |
| 16 | | pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { |
| 16 | pub fn format( |
| 17 | context: var, |
| 18 | comptime Errors: type, |
| 19 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 20 | comptime fmt: []const u8, |
| 21 | args: ..., |
| 22 | ) Errors!void { |
| 17 | 23 | const State = enum { |
| 18 | 24 | Start, |
| 19 | 25 | OpenBrace, |
| ... | ... | @@ -28,7 +34,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 28 | 34 | |
| 29 | 35 | inline for (fmt) |c, i| { |
| 30 | 36 | switch (state) { |
| 31 | | State.Start => switch (c) { |
| 37 | .Start => switch (c) { |
| 32 | 38 | '{' => { |
| 33 | 39 | if (start_index < i) { |
| 34 | 40 | try output(context, fmt[start_index..i]); |
| ... | ... | @@ -45,7 +51,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 45 | 51 | }, |
| 46 | 52 | else => {}, |
| 47 | 53 | }, |
| 48 | | State.OpenBrace => switch (c) { |
| 54 | .OpenBrace => switch (c) { |
| 49 | 55 | '{' => { |
| 50 | 56 | state = State.Start; |
| 51 | 57 | start_index = i; |
| ... | ... | @@ -61,14 +67,14 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 61 | 67 | state = State.FormatString; |
| 62 | 68 | }, |
| 63 | 69 | }, |
| 64 | | State.CloseBrace => switch (c) { |
| 70 | .CloseBrace => switch (c) { |
| 65 | 71 | '}' => { |
| 66 | 72 | state = State.Start; |
| 67 | 73 | start_index = i; |
| 68 | 74 | }, |
| 69 | 75 | else => @compileError("Single '}' encountered in format string"), |
| 70 | 76 | }, |
| 71 | | State.FormatString => switch (c) { |
| 77 | .FormatString => switch (c) { |
| 72 | 78 | '}' => { |
| 73 | 79 | const s = start_index + 1; |
| 74 | 80 | try formatType(args[next_arg], fmt[s..i], context, Errors, output, default_max_depth); |
| ... | ... | @@ -78,7 +84,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 78 | 84 | }, |
| 79 | 85 | else => {}, |
| 80 | 86 | }, |
| 81 | | State.Pointer => switch (c) { |
| 87 | .Pointer => switch (c) { |
| 82 | 88 | '}' => { |
| 83 | 89 | try output(context, @typeName(@typeOf(args[next_arg]).Child)); |
| 84 | 90 | try output(context, "@"); |
| ... | ... | @@ -114,88 +120,93 @@ pub fn formatType( |
| 114 | 120 | ) Errors!void { |
| 115 | 121 | const T = @typeOf(value); |
| 116 | 122 | switch (@typeInfo(T)) { |
| 117 | | builtin.TypeId.ComptimeInt, builtin.TypeId.Int, builtin.TypeId.Float => { |
| 123 | .ComptimeInt, .Int, .Float => { |
| 118 | 124 | return formatValue(value, fmt, context, Errors, output); |
| 119 | 125 | }, |
| 120 | | builtin.TypeId.Void => { |
| 126 | .Void => { |
| 121 | 127 | return output(context, "void"); |
| 122 | 128 | }, |
| 123 | | builtin.TypeId.Bool => { |
| 129 | .Bool => { |
| 124 | 130 | return output(context, if (value) "true" else "false"); |
| 125 | 131 | }, |
| 126 | | builtin.TypeId.Optional => { |
| 132 | .Optional => { |
| 127 | 133 | if (value) |payload| { |
| 128 | 134 | return formatType(payload, fmt, context, Errors, output, max_depth); |
| 129 | 135 | } else { |
| 130 | 136 | return output(context, "null"); |
| 131 | 137 | } |
| 132 | 138 | }, |
| 133 | | builtin.TypeId.ErrorUnion => { |
| 139 | .ErrorUnion => { |
| 134 | 140 | if (value) |payload| { |
| 135 | 141 | return formatType(payload, fmt, context, Errors, output, max_depth); |
| 136 | 142 | } else |err| { |
| 137 | 143 | return formatType(err, fmt, context, Errors, output, max_depth); |
| 138 | 144 | } |
| 139 | 145 | }, |
| 140 | | builtin.TypeId.ErrorSet => { |
| 146 | .ErrorSet => { |
| 141 | 147 | try output(context, "error."); |
| 142 | 148 | return output(context, @errorName(value)); |
| 143 | 149 | }, |
| 144 | | builtin.TypeId.Promise => { |
| 150 | .Promise => { |
| 145 | 151 | return format(context, Errors, output, "promise@{x}", @ptrToInt(value)); |
| 146 | 152 | }, |
| 147 | | builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { |
| 148 | | if (comptime std.meta.trait.hasFn("format")(T)) return value.format(fmt, context, Errors, output); |
| 153 | .Enum => { |
| 154 | if (comptime std.meta.trait.hasFn("format")(T)) { |
| 155 | return value.format(fmt, context, Errors, output); |
| 156 | } |
| 149 | 157 | |
| 150 | 158 | try output(context, @typeName(T)); |
| 151 | | switch (comptime @typeId(T)) { |
| 152 | | builtin.TypeId.Enum => { |
| 153 | | try output(context, "."); |
| 154 | | try formatType(@tagName(value), "", context, Errors, output, max_depth); |
| 155 | | return; |
| 156 | | }, |
| 157 | | builtin.TypeId.Struct => { |
| 158 | | if (max_depth == 0) { |
| 159 | | return output(context, "{ ... }"); |
| 160 | | } |
| 161 | | comptime var field_i = 0; |
| 162 | | inline while (field_i < @memberCount(T)) : (field_i += 1) { |
| 163 | | if (field_i == 0) { |
| 164 | | try output(context, "{ ."); |
| 165 | | } else { |
| 166 | | try output(context, ", ."); |
| 167 | | } |
| 168 | | try output(context, @memberName(T, field_i)); |
| 169 | | try output(context, " = "); |
| 170 | | try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth - 1); |
| 171 | | } |
| 172 | | try output(context, " }"); |
| 173 | | }, |
| 174 | | builtin.TypeId.Union => { |
| 175 | | if (max_depth == 0) { |
| 176 | | return output(context, "{ ... }"); |
| 177 | | } |
| 178 | | const info = @typeInfo(T).Union; |
| 179 | | if (info.tag_type) |UnionTagType| { |
| 180 | | try output(context, "{ ."); |
| 181 | | try output(context, @tagName(UnionTagType(value))); |
| 182 | | try output(context, " = "); |
| 183 | | inline for (info.fields) |u_field| { |
| 184 | | if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { |
| 185 | | try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth - 1); |
| 186 | | } |
| 187 | | } |
| 188 | | try output(context, " }"); |
| 189 | | } else { |
| 190 | | try format(context, Errors, output, "@{x}", @ptrToInt(&value)); |
| 159 | try output(context, "."); |
| 160 | return formatType(@tagName(value), "", context, Errors, output, max_depth); |
| 161 | }, |
| 162 | .Union => { |
| 163 | if (comptime std.meta.trait.hasFn("format")(T)) { |
| 164 | return value.format(fmt, context, Errors, output); |
| 165 | } |
| 166 | |
| 167 | try output(context, @typeName(T)); |
| 168 | if (max_depth == 0) { |
| 169 | return output(context, "{ ... }"); |
| 170 | } |
| 171 | const info = @typeInfo(T).Union; |
| 172 | if (info.tag_type) |UnionTagType| { |
| 173 | try output(context, "{ ."); |
| 174 | try output(context, @tagName(UnionTagType(value))); |
| 175 | try output(context, " = "); |
| 176 | inline for (info.fields) |u_field| { |
| 177 | if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { |
| 178 | try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth - 1); |
| 191 | 179 | } |
| 192 | | }, |
| 193 | | else => unreachable, |
| 180 | } |
| 181 | try output(context, " }"); |
| 182 | } else { |
| 183 | try format(context, Errors, output, "@{x}", @ptrToInt(&value)); |
| 194 | 184 | } |
| 195 | | return; |
| 196 | 185 | }, |
| 197 | | builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) { |
| 198 | | builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) { |
| 186 | .Struct => { |
| 187 | if (comptime std.meta.trait.hasFn("format")(T)) { |
| 188 | return value.format(fmt, context, Errors, output); |
| 189 | } |
| 190 | |
| 191 | try output(context, @typeName(T)); |
| 192 | if (max_depth == 0) { |
| 193 | return output(context, "{ ... }"); |
| 194 | } |
| 195 | comptime var field_i = 0; |
| 196 | inline while (field_i < @memberCount(T)) : (field_i += 1) { |
| 197 | if (field_i == 0) { |
| 198 | try output(context, "{ ."); |
| 199 | } else { |
| 200 | try output(context, ", ."); |
| 201 | } |
| 202 | try output(context, @memberName(T, field_i)); |
| 203 | try output(context, " = "); |
| 204 | try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth - 1); |
| 205 | } |
| 206 | try output(context, " }"); |
| 207 | }, |
| 208 | .Pointer => |ptr_info| switch (ptr_info.size) { |
| 209 | .One => switch (@typeInfo(ptr_info.child)) { |
| 199 | 210 | builtin.TypeId.Array => |info| { |
| 200 | 211 | if (info.child == u8) { |
| 201 | 212 | return formatText(value, fmt, context, Errors, output); |
| ... | ... | @@ -207,7 +218,7 @@ pub fn formatType( |
| 207 | 218 | }, |
| 208 | 219 | else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), |
| 209 | 220 | }, |
| 210 | | builtin.TypeInfo.Pointer.Size.Many => { |
| 221 | .Many => { |
| 211 | 222 | if (ptr_info.child == u8) { |
| 212 | 223 | if (fmt.len > 0 and fmt[0] == 's') { |
| 213 | 224 | const len = mem.len(u8, value); |
| ... | ... | @@ -216,7 +227,7 @@ pub fn formatType( |
| 216 | 227 | } |
| 217 | 228 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); |
| 218 | 229 | }, |
| 219 | | builtin.TypeInfo.Pointer.Size.Slice => { |
| 230 | .Slice => { |
| 220 | 231 | if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { |
| 221 | 232 | return formatText(value, fmt, context, Errors, output); |
| 222 | 233 | } |
| ... | ... | @@ -225,17 +236,17 @@ pub fn formatType( |
| 225 | 236 | } |
| 226 | 237 | return format(context, Errors, output, "{}@{x}", @typeName(ptr_info.child), @ptrToInt(value.ptr)); |
| 227 | 238 | }, |
| 228 | | builtin.TypeInfo.Pointer.Size.C => { |
| 239 | .C => { |
| 229 | 240 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); |
| 230 | 241 | }, |
| 231 | 242 | }, |
| 232 | | builtin.TypeId.Array => |info| { |
| 243 | .Array => |info| { |
| 233 | 244 | if (info.child == u8) { |
| 234 | 245 | return formatText(value, fmt, context, Errors, output); |
| 235 | 246 | } |
| 236 | 247 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); |
| 237 | 248 | }, |
| 238 | | builtin.TypeId.Fn => { |
| 249 | .Fn => { |
| 239 | 250 | return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value)); |
| 240 | 251 | }, |
| 241 | 252 | else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), |
| ... | ... | @@ -249,24 +260,24 @@ fn formatValue( |
| 249 | 260 | comptime Errors: type, |
| 250 | 261 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 251 | 262 | ) Errors!void { |
| 252 | | if (fmt.len > 0) { |
| 253 | | if (fmt[0] == 'B') { |
| 254 | | comptime var width: ?usize = null; |
| 255 | | if (fmt.len > 1) { |
| 256 | | if (fmt[1] == 'i') { |
| 257 | | if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); |
| 258 | | return formatBytes(value, width, 1024, context, Errors, output); |
| 263 | if (fmt.len > 0 and fmt[0] == 'B') { |
| 264 | comptime var width: ?usize = null; |
| 265 | if (fmt.len > 1) { |
| 266 | if (fmt[1] == 'i') { |
| 267 | if (fmt.len > 2) { |
| 268 | width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); |
| 259 | 269 | } |
| 260 | | width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); |
| 270 | return formatBytes(value, width, 1024, context, Errors, output); |
| 261 | 271 | } |
| 262 | | return formatBytes(value, width, 1000, context, Errors, output); |
| 272 | width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); |
| 263 | 273 | } |
| 274 | return formatBytes(value, width, 1000, context, Errors, output); |
| 264 | 275 | } |
| 265 | 276 | |
| 266 | 277 | const T = @typeOf(value); |
| 267 | 278 | switch (@typeId(T)) { |
| 268 | | builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output), |
| 269 | | builtin.TypeId.Int, builtin.TypeId.ComptimeInt => return formatIntValue(value, fmt, context, Errors, output), |
| 279 | .Float => return formatFloatValue(value, fmt, context, Errors, output), |
| 280 | .Int, .ComptimeInt => return formatIntValue(value, fmt, context, Errors, output), |
| 270 | 281 | else => comptime unreachable, |
| 271 | 282 | } |
| 272 | 283 | } |
| ... | ... | @@ -797,7 +808,7 @@ pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { |
| 797 | 808 | } |
| 798 | 809 | } |
| 799 | 810 | |
| 800 | | test "fmt.parseInt" { |
| 811 | test "parseInt" { |
| 801 | 812 | testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10); |
| 802 | 813 | testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10); |
| 803 | 814 | testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); |
| ... | ... | @@ -828,7 +839,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned |
| 828 | 839 | return x; |
| 829 | 840 | } |
| 830 | 841 | |
| 831 | | test "fmt.parseUnsigned" { |
| 842 | test "parseUnsigned" { |
| 832 | 843 | testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); |
| 833 | 844 | testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); |
| 834 | 845 | testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); |
| ... | ... | @@ -913,7 +924,7 @@ fn countSize(size: *usize, bytes: []const u8) (error{}!void) { |
| 913 | 924 | size.* += bytes.len; |
| 914 | 925 | } |
| 915 | 926 | |
| 916 | | test "buf print int" { |
| 927 | test "bufPrintInt" { |
| 917 | 928 | var buffer: [100]u8 = undefined; |
| 918 | 929 | const buf = buffer[0..]; |
| 919 | 930 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); |
| ... | ... | @@ -949,7 +960,7 @@ test "parse unsigned comptime" { |
| 949 | 960 | } |
| 950 | 961 | } |
| 951 | 962 | |
| 952 | | test "fmt.format" { |
| 963 | test "fmt.optional" { |
| 953 | 964 | { |
| 954 | 965 | const value: ?i32 = 1234; |
| 955 | 966 | try testFmt("optional: 1234\n", "optional: {}\n", value); |
| ... | ... | @@ -958,6 +969,9 @@ test "fmt.format" { |
| 958 | 969 | const value: ?i32 = null; |
| 959 | 970 | try testFmt("optional: null\n", "optional: {}\n", value); |
| 960 | 971 | } |
| 972 | } |
| 973 | |
| 974 | test "fmt.error" { |
| 961 | 975 | { |
| 962 | 976 | const value: anyerror!i32 = 1234; |
| 963 | 977 | try testFmt("error union: 1234\n", "error union: {}\n", value); |
| ... | ... | @@ -966,10 +980,16 @@ test "fmt.format" { |
| 966 | 980 | const value: anyerror!i32 = error.InvalidChar; |
| 967 | 981 | try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value); |
| 968 | 982 | } |
| 983 | } |
| 984 | |
| 985 | test "fmt.int.small" { |
| 969 | 986 | { |
| 970 | 987 | const value: u3 = 0b101; |
| 971 | 988 | try testFmt("u3: 5\n", "u3: {}\n", value); |
| 972 | 989 | } |
| 990 | } |
| 991 | |
| 992 | test "fmt.int.specifier" { |
| 973 | 993 | { |
| 974 | 994 | const value: u8 = 'a'; |
| 975 | 995 | try testFmt("u8: a\n", "u8: {c}\n", value); |
| ... | ... | @@ -978,6 +998,9 @@ test "fmt.format" { |
| 978 | 998 | const value: u8 = 0b1100; |
| 979 | 999 | try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value); |
| 980 | 1000 | } |
| 1001 | } |
| 1002 | |
| 1003 | test "fmt.buffer" { |
| 981 | 1004 | { |
| 982 | 1005 | var buf1: [32]u8 = undefined; |
| 983 | 1006 | var context = BufPrintContext{ .remaining = buf1[0..] }; |
| ... | ... | @@ -995,6 +1018,9 @@ test "fmt.format" { |
| 995 | 1018 | res = buf1[0 .. buf1.len - context.remaining.len]; |
| 996 | 1019 | testing.expect(mem.eql(u8, res, "1100")); |
| 997 | 1020 | } |
| 1021 | } |
| 1022 | |
| 1023 | test "fmt.array" { |
| 998 | 1024 | { |
| 999 | 1025 | const value: [3]u8 = "abc"; |
| 1000 | 1026 | try testFmt("array: abc\n", "array: {}\n", value); |
| ... | ... | @@ -1007,6 +1033,9 @@ test "fmt.format" { |
| 1007 | 1033 | &value, |
| 1008 | 1034 | ); |
| 1009 | 1035 | } |
| 1036 | } |
| 1037 | |
| 1038 | test "fmt.slice" { |
| 1010 | 1039 | { |
| 1011 | 1040 | const value: []const u8 = "abc"; |
| 1012 | 1041 | try testFmt("slice: abc\n", "slice: {}\n", value); |
| ... | ... | @@ -1015,6 +1044,12 @@ test "fmt.format" { |
| 1015 | 1044 | const value = @intToPtr([*]const []const u8, 0xdeadbeef)[0..0]; |
| 1016 | 1045 | try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", value); |
| 1017 | 1046 | } |
| 1047 | |
| 1048 | try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); |
| 1049 | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); |
| 1050 | } |
| 1051 | |
| 1052 | test "fmt.pointer" { |
| 1018 | 1053 | { |
| 1019 | 1054 | const value = @intToPtr(*i32, 0xdeadbeef); |
| 1020 | 1055 | try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); |
| ... | ... | @@ -1028,12 +1063,19 @@ test "fmt.format" { |
| 1028 | 1063 | const value = @intToPtr(fn () void, 0xdeadbeef); |
| 1029 | 1064 | try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); |
| 1030 | 1065 | } |
| 1031 | | try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); |
| 1032 | | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); |
| 1066 | } |
| 1067 | |
| 1068 | test "fmt.cstr" { |
| 1033 | 1069 | try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); |
| 1034 | 1070 | try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C"); |
| 1071 | } |
| 1072 | |
| 1073 | test "fmt.filesize" { |
| 1035 | 1074 | try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); |
| 1036 | 1075 | try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); |
| 1076 | } |
| 1077 | |
| 1078 | test "fmt.struct" { |
| 1037 | 1079 | { |
| 1038 | 1080 | const Struct = struct { |
| 1039 | 1081 | field: u8, |
| ... | ... | @@ -1050,15 +1092,19 @@ test "fmt.format" { |
| 1050 | 1092 | const value = Struct{ .a = 0, .b = 1 }; |
| 1051 | 1093 | try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", value); |
| 1052 | 1094 | } |
| 1053 | | { |
| 1054 | | const Enum = enum { |
| 1055 | | One, |
| 1056 | | Two, |
| 1057 | | }; |
| 1058 | | const value = Enum.Two; |
| 1059 | | try testFmt("enum: Enum.Two\n", "enum: {}\n", value); |
| 1060 | | try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); |
| 1061 | | } |
| 1095 | } |
| 1096 | |
| 1097 | test "fmt.enum" { |
| 1098 | const Enum = enum { |
| 1099 | One, |
| 1100 | Two, |
| 1101 | }; |
| 1102 | const value = Enum.Two; |
| 1103 | try testFmt("enum: Enum.Two\n", "enum: {}\n", value); |
| 1104 | try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); |
| 1105 | } |
| 1106 | |
| 1107 | test "fmt.float.scientific" { |
| 1062 | 1108 | { |
| 1063 | 1109 | var buf1: [32]u8 = undefined; |
| 1064 | 1110 | const value: f32 = 1.34; |
| ... | ... | @@ -1088,6 +1134,9 @@ test "fmt.format" { |
| 1088 | 1134 | testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n")); |
| 1089 | 1135 | } |
| 1090 | 1136 | } |
| 1137 | } |
| 1138 | |
| 1139 | test "fmt.float.scientific.precision" { |
| 1091 | 1140 | { |
| 1092 | 1141 | var buf1: [32]u8 = undefined; |
| 1093 | 1142 | const value: f64 = 1.409706e-42; |
| ... | ... | @@ -1114,6 +1163,9 @@ test "fmt.format" { |
| 1114 | 1163 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); |
| 1115 | 1164 | testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n")); |
| 1116 | 1165 | } |
| 1166 | } |
| 1167 | |
| 1168 | test "fmt.float.special" { |
| 1117 | 1169 | { |
| 1118 | 1170 | var buf1: [32]u8 = undefined; |
| 1119 | 1171 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); |
| ... | ... | @@ -1136,6 +1188,9 @@ test "fmt.format" { |
| 1136 | 1188 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); |
| 1137 | 1189 | testing.expect(mem.eql(u8, result, "f64: -inf\n")); |
| 1138 | 1190 | } |
| 1191 | } |
| 1192 | |
| 1193 | test "fmt.float.decimal" { |
| 1139 | 1194 | { |
| 1140 | 1195 | var buf1: [64]u8 = undefined; |
| 1141 | 1196 | const value: f64 = 1.52314e+29; |
| ... | ... | @@ -1216,7 +1271,9 @@ test "fmt.format" { |
| 1216 | 1271 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 1217 | 1272 | testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); |
| 1218 | 1273 | } |
| 1219 | | // libc checks |
| 1274 | } |
| 1275 | |
| 1276 | test "fmt.float.libc.sanity" { |
| 1220 | 1277 | { |
| 1221 | 1278 | var buf1: [32]u8 = undefined; |
| 1222 | 1279 | const value: f64 = f64(@bitCast(f32, u32(916964781))); |
| ... | ... | @@ -1267,127 +1324,127 @@ test "fmt.format" { |
| 1267 | 1324 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); |
| 1268 | 1325 | testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); |
| 1269 | 1326 | } |
| 1270 | | //custom type format |
| 1271 | | { |
| 1272 | | const Vec2 = struct { |
| 1273 | | const SelfType = @This(); |
| 1274 | | x: f32, |
| 1275 | | y: f32, |
| 1276 | | |
| 1277 | | pub fn format( |
| 1278 | | self: SelfType, |
| 1279 | | comptime fmt: []const u8, |
| 1280 | | context: var, |
| 1281 | | comptime Errors: type, |
| 1282 | | output: fn (@typeOf(context), []const u8) Errors!void, |
| 1283 | | ) Errors!void { |
| 1284 | | switch (fmt.len) { |
| 1285 | | 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), |
| 1286 | | 1 => switch (fmt[0]) { |
| 1287 | | //point format |
| 1288 | | 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), |
| 1289 | | //dimension format |
| 1290 | | 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), |
| 1291 | | else => unreachable, |
| 1292 | | }, |
| 1327 | } |
| 1328 | |
| 1329 | test "fmt.custom" { |
| 1330 | const Vec2 = struct { |
| 1331 | const SelfType = @This(); |
| 1332 | x: f32, |
| 1333 | y: f32, |
| 1334 | |
| 1335 | pub fn format( |
| 1336 | self: SelfType, |
| 1337 | comptime fmt: []const u8, |
| 1338 | context: var, |
| 1339 | comptime Errors: type, |
| 1340 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 1341 | ) Errors!void { |
| 1342 | switch (fmt.len) { |
| 1343 | 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), |
| 1344 | 1 => switch (fmt[0]) { |
| 1345 | //point format |
| 1346 | 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), |
| 1347 | //dimension format |
| 1348 | 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), |
| 1293 | 1349 | else => unreachable, |
| 1294 | | } |
| 1350 | }, |
| 1351 | else => unreachable, |
| 1295 | 1352 | } |
| 1296 | | }; |
| 1353 | } |
| 1354 | }; |
| 1297 | 1355 | |
| 1298 | | var buf1: [32]u8 = undefined; |
| 1299 | | var value = Vec2{ |
| 1300 | | .x = 10.2, |
| 1301 | | .y = 2.22, |
| 1302 | | }; |
| 1303 | | try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); |
| 1304 | | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); |
| 1356 | var buf1: [32]u8 = undefined; |
| 1357 | var value = Vec2{ |
| 1358 | .x = 10.2, |
| 1359 | .y = 2.22, |
| 1360 | }; |
| 1361 | try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); |
| 1362 | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); |
| 1305 | 1363 | |
| 1306 | | // same thing but not passing a pointer |
| 1307 | | try testFmt("point: (10.200,2.220)\n", "point: {}\n", value); |
| 1308 | | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); |
| 1309 | | } |
| 1310 | | //struct format |
| 1311 | | { |
| 1312 | | const S = struct { |
| 1313 | | a: u32, |
| 1314 | | b: anyerror, |
| 1315 | | }; |
| 1364 | // same thing but not passing a pointer |
| 1365 | try testFmt("point: (10.200,2.220)\n", "point: {}\n", value); |
| 1366 | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); |
| 1367 | } |
| 1316 | 1368 | |
| 1317 | | const inst = S{ |
| 1318 | | .a = 456, |
| 1319 | | .b = error.Unused, |
| 1320 | | }; |
| 1369 | test "fmt.struct" { |
| 1370 | const S = struct { |
| 1371 | a: u32, |
| 1372 | b: anyerror, |
| 1373 | }; |
| 1321 | 1374 | |
| 1322 | | try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); |
| 1323 | | } |
| 1324 | | //union format |
| 1325 | | { |
| 1326 | | const TU = union(enum) { |
| 1327 | | float: f32, |
| 1328 | | int: u32, |
| 1329 | | }; |
| 1375 | const inst = S{ |
| 1376 | .a = 456, |
| 1377 | .b = error.Unused, |
| 1378 | }; |
| 1330 | 1379 | |
| 1331 | | const UU = union { |
| 1332 | | float: f32, |
| 1333 | | int: u32, |
| 1334 | | }; |
| 1380 | try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); |
| 1381 | } |
| 1335 | 1382 | |
| 1336 | | const EU = extern union { |
| 1337 | | float: f32, |
| 1338 | | int: u32, |
| 1339 | | }; |
| 1383 | test "fmt.union" { |
| 1384 | const TU = union(enum) { |
| 1385 | float: f32, |
| 1386 | int: u32, |
| 1387 | }; |
| 1340 | 1388 | |
| 1341 | | const tu_inst = TU{ .int = 123 }; |
| 1342 | | const uu_inst = UU{ .int = 456 }; |
| 1343 | | const eu_inst = EU{ .float = 321.123 }; |
| 1389 | const UU = union { |
| 1390 | float: f32, |
| 1391 | int: u32, |
| 1392 | }; |
| 1344 | 1393 | |
| 1345 | | try testFmt("TU{ .int = 123 }", "{}", tu_inst); |
| 1394 | const EU = extern union { |
| 1395 | float: f32, |
| 1396 | int: u32, |
| 1397 | }; |
| 1346 | 1398 | |
| 1347 | | var buf: [100]u8 = undefined; |
| 1348 | | const uu_result = try bufPrint(buf[0..], "{}", uu_inst); |
| 1349 | | testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); |
| 1399 | const tu_inst = TU{ .int = 123 }; |
| 1400 | const uu_inst = UU{ .int = 456 }; |
| 1401 | const eu_inst = EU{ .float = 321.123 }; |
| 1350 | 1402 | |
| 1351 | | const eu_result = try bufPrint(buf[0..], "{}", eu_inst); |
| 1352 | | testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); |
| 1353 | | } |
| 1354 | | //enum format |
| 1355 | | { |
| 1356 | | const E = enum { |
| 1357 | | One, |
| 1358 | | Two, |
| 1359 | | Three, |
| 1360 | | }; |
| 1403 | try testFmt("TU{ .int = 123 }", "{}", tu_inst); |
| 1361 | 1404 | |
| 1362 | | const inst = E.Two; |
| 1405 | var buf: [100]u8 = undefined; |
| 1406 | const uu_result = try bufPrint(buf[0..], "{}", uu_inst); |
| 1407 | testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); |
| 1363 | 1408 | |
| 1364 | | try testFmt("E.Two", "{}", inst); |
| 1365 | | } |
| 1366 | | //self-referential struct format |
| 1367 | | { |
| 1368 | | const S = struct { |
| 1369 | | const SelfType = @This(); |
| 1370 | | a: ?*SelfType, |
| 1371 | | }; |
| 1409 | const eu_result = try bufPrint(buf[0..], "{}", eu_inst); |
| 1410 | testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); |
| 1411 | } |
| 1372 | 1412 | |
| 1373 | | var inst = S{ |
| 1374 | | .a = null, |
| 1375 | | }; |
| 1376 | | inst.a = &inst; |
| 1413 | test "fmt.enum" { |
| 1414 | const E = enum { |
| 1415 | One, |
| 1416 | Two, |
| 1417 | Three, |
| 1418 | }; |
| 1377 | 1419 | |
| 1378 | | try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst); |
| 1379 | | } |
| 1380 | | //print bytes as hex |
| 1381 | | { |
| 1382 | | const some_bytes = "\xCA\xFE\xBA\xBE"; |
| 1383 | | try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); |
| 1384 | | try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); |
| 1385 | | //Test Slices |
| 1386 | | try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]); |
| 1387 | | try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]); |
| 1388 | | const bytes_with_zeros = "\x00\x0E\xBA\xBE"; |
| 1389 | | try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros); |
| 1390 | | } |
| 1420 | const inst = E.Two; |
| 1421 | |
| 1422 | try testFmt("E.Two", "{}", inst); |
| 1423 | } |
| 1424 | |
| 1425 | test "fmt.struct.self-referential" { |
| 1426 | const S = struct { |
| 1427 | const SelfType = @This(); |
| 1428 | a: ?*SelfType, |
| 1429 | }; |
| 1430 | |
| 1431 | var inst = S{ |
| 1432 | .a = null, |
| 1433 | }; |
| 1434 | inst.a = &inst; |
| 1435 | |
| 1436 | try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst); |
| 1437 | } |
| 1438 | |
| 1439 | test "fmt.bytes.hex" { |
| 1440 | const some_bytes = "\xCA\xFE\xBA\xBE"; |
| 1441 | try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); |
| 1442 | try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); |
| 1443 | //Test Slices |
| 1444 | try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]); |
| 1445 | try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]); |
| 1446 | const bytes_with_zeros = "\x00\x0E\xBA\xBE"; |
| 1447 | try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros); |
| 1391 | 1448 | } |
| 1392 | 1449 | |
| 1393 | 1450 | fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { |