ona/src/coral/utf8.zig

126 lines
3.3 KiB
Zig
Raw Normal View History

const ascii = @import("./ascii.zig");
const coral = @import("./coral.zig");
const io = @import("./io.zig");
const std = @import("std");
pub fn alloc_formatted(allocator: std.mem.Allocator, comptime format: []const u8, args: anytype) std.mem.Allocator.Error![]coral.Byte {
var buffer = coral.Stack(coral.Byte){.allocator = allocator};
const formatted_len = count_formatted(format, args);
try buffer.grow(formatted_len);
errdefer buffer.deinit();
print_formatted(buffer.writer(), format, args) catch unreachable;
return buffer.to_allocation(formatted_len, 0);
}
fn count_formatted(comptime format: []const u8, args: anytype) usize {
var count = io.defaultWritable{};
print_formatted(count.writer(), format, args) catch unreachable;
return count.written;
}
pub fn print_formatted(writer: io.Writer, comptime format: []const u8, args: anytype) io.PrintError!void {
switch (@typeInfo(@TypeOf(args))) {
.Struct => |arguments_struct| {
comptime var arg_index = 0;
comptime var head = 0;
comptime var tail = 0;
inline while (tail < format.len) : (tail += 1) {
if (format[tail] == '{') {
if (tail > format.len) {
@compileError("expected an idenifier after opening `{`");
}
tail += 1;
switch (format[tail]) {
'{' => {
try io.print(writer, format[head .. (tail - 1)]);
tail += 1;
head = tail;
},
'}' => {
if (!arguments_struct.is_tuple) {
@compileError("all format specifiers must be named when using a named struct");
}
try io.print(writer, args[arg_index]);
arg_index += 1;
tail += 1;
head = tail;
},
else => {
if (arguments_struct.is_tuple) {
@compileError("format specifiers cannot be named when using a tuple struct");
}
try io.print(writer, format[head .. (tail - 1)]);
head = tail;
tail += 1;
if (tail >= format.len) {
@compileError("expected closing `}` or another `{` after opening `{`");
}
std.debug.assert(tail < format.len);
inline while (format[tail] != '}') {
tail += 1;
std.debug.assert(tail < format.len);
}
try print_formatted_value(writer, @field(args, format[head .. tail]));
tail += 1;
head = tail;
}
}
}
}
try io.print(writer, format[head .. ]);
},
else => @compileError("`arguments` must be a struct type"),
}
}
noinline fn print_formatted_value(writer: io.Writer, value: anytype) io.PrintError!void {
const Value = @TypeOf(value);
return switch (@typeInfo(Value)) {
.Int => ascii.DecimalFormat.default.print(writer, value),
.Float => ascii.DecimalFormat.default.print(writer, value),
.Enum => io.print(writer, @tagName(value)),
.Pointer => |pointer| switch (pointer.size) {
.Many, .C => ascii.HexadecimalFormat.default.print(writer, @intFromPtr(value)),
.One => if (pointer.child == []const u8) io.print(writer, *value) else ascii.HexadecimalFormat.default.print(writer, @intFromPtr(value)),
.Slice => if (pointer.child == u8) io.print(writer, value) else @compileError(unformattableMessage(Value)),
},
else => @compileError(unformattableMessage(Value)),
};
}
const root = @This();
fn unformattableMessage(comptime Value: type) []const u8 {
return "type `" ++ @typeName(Value) ++ "` is not formattable with this formatter";
}