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