const std = @import("std"); // Must be consistent with `std.io.tty.Color` for Windows compatibility. pub const codes = .{ .escape = "\x1b[", .black = "30m", .red = "31m", .green = "32m", .yellow = "33m", .blue = "34m", .magenta = "35m", .cyan = "36m", .white = "37m", .bright_black = "90m", .bright_red = "91m", .bright_green = "92m", .bright_yellow = "93m", .bright_blue = "94m", .bright_magenta = "95m", .bright_cyan = "96m", .bright_white = "97m", .bold = "1m", .dim = "2m", .reset = "0m", }; /// Map color codes generated by `std.io.tty.Config.setColor` back to `std.io.tty.Color`. Used by /// `jetzig.util.writeAnsi` to parse escape codes so they can be passed to /// `std.io.tty.Config.setColor` (using Windows API to set console color mode). const ansi_colors = .{ .{ "30", .black }, .{ "31", .red }, .{ "32", .green }, .{ "33", .yellow }, .{ "34", .blue }, .{ "35", .magenta }, .{ "36", .cyan }, .{ "37", .white }, .{ "90", .bright_black }, .{ "91", .bright_red }, .{ "92", .bright_green }, .{ "93", .bright_yellow }, .{ "94", .bright_blue }, .{ "95", .bright_magenta }, .{ "96", .bright_cyan }, .{ "97", .bright_white }, .{ "1", .bold }, .{ "2", .dim }, .{ "0", .reset }, }; pub const codes_map = if (@hasDecl(std, "ComptimeStringMap")) std.ComptimeStringMap(std.io.tty.Color, ansi_colors) else if (@hasDecl(std, "StaticStringMap")) std.StaticStringMap(std.io.tty.Color).initComptime(ansi_colors) else unreachable; // Map basic ANSI color codes to Windows TextAttribute colors // used by std.os.windows.SetConsoleTextAttribute() const windows_colors = .{ .{ "30", 0 }, .{ "31", 4 }, .{ "32", 2 }, .{ "33", 6 }, .{ "34", 1 }, .{ "35", 5 }, .{ "36", 3 }, .{ "37", 7 }, .{ "90", 8 }, .{ "91", 12 }, .{ "92", 10 }, .{ "93", 14 }, .{ "94", 9 }, .{ "95", 13 }, .{ "96", 11 }, .{ "97", 15 }, .{ "1", 7 }, .{ "2", 7 }, .{ "0", 7 }, }; pub const windows_map = if (@hasDecl(std, "ComptimeStringMap")) std.ComptimeStringMap(u16, windows_colors) else if (@hasDecl(std, "StaticStringMap")) std.StaticStringMap(u16).initComptime(windows_colors) else unreachable; /// Colorize a log message. Note that we force `.escape_codes` when we are a TTY even on Windows. /// `jetzig.loggers.LogQueue` parses the ANSI codes and uses `std.io.tty.Config.setColor` to /// invoke the appropriate Windows API call to set the terminal color before writing each token. /// We must do it this way because Windows colors are set by API calls at the time of write, not /// encoded into the message string. pub fn colorize(color: std.io.tty.Color, buf: []u8, input: []const u8, is_colorized: bool) ![]const u8 { if (!is_colorized) return input; const config: std.io.tty.Config = .escape_codes; var stream = std.io.fixedBufferStream(buf); const writer = stream.writer(); try config.setColor(writer, color); try writer.writeAll(input); try config.setColor(writer, .reset); return stream.getWritten(); } fn wrap(comptime attribute: []const u8, comptime message: []const u8) []const u8 { return codes.escape ++ attribute ++ message ++ codes.escape ++ codes.reset; } fn runtimeWrap(allocator: std.mem.Allocator, attribute: []const u8, message: []const u8) ![]const u8 { return try std.mem.join( allocator, "", &[_][]const u8{ codes.escape, attribute, message, codes.escape, codes.reset }, ); } pub fn bold(comptime message: []const u8) []const u8 { return codes.escape ++ codes.bold ++ message ++ codes.escape ++ codes.reset; } pub fn black(comptime message: []const u8) []const u8 { return wrap(codes.black, message); } pub fn runtimeBlack(allocator: std.mem.Allocator, message: []const u8) ![]const u8 { return try runtimeWrap(allocator, codes.black, message); } pub fn red(comptime message: []const u8) []const u8 { return wrap(codes.red, message); } pub fn runtimeRed(allocator: std.mem.Allocator, message: []const u8) ![]const u8 { return try runtimeWrap(allocator, codes.red, message); } pub fn green(comptime message: []const u8) []const u8 { return wrap(codes.green, message); } pub fn runtimeGreen(allocator: std.mem.Allocator, message: []const u8) ![]const u8 { return try runtimeWrap(allocator, codes.green, message); } pub fn yellow(comptime message: []const u8) []const u8 { return wrap(codes.yellow, message); } pub fn runtimeYellow(allocator: std.mem.Allocator, message: []const u8) ![]const u8 { return try runtimeWrap(allocator, codes.yellow, message); } pub fn blue(comptime message: []const u8) []const u8 { return wrap(codes.blue, message); } pub fn runtimeBlue(allocator: std.mem.Allocator, message: []const u8) ![]const u8 { return try runtimeWrap(allocator, codes.blue, message); } pub fn magenta(comptime message: []const u8) []const u8 { return wrap(codes.magenta, message); } pub fn runtimeMagenta(allocator: std.mem.Allocator, message: []const u8) ![]const u8 { return try runtimeWrap(allocator, codes.magenta, message); } pub fn cyan(comptime message: []const u8) []const u8 { return wrap(codes.cyan, message); } pub fn runtimeCyan(allocator: std.mem.Allocator, message: []const u8) ![]const u8 { return try runtimeWrap(allocator, codes.cyan, message); } pub fn white(comptime message: []const u8) []const u8 { return wrap(codes.white, message); } pub fn runtimeWhite(allocator: std.mem.Allocator, message: []const u8) ![]const u8 { return try runtimeWrap(allocator, codes.white, message); } pub fn duration(buf: *[256]u8, delta: i64, is_colorized: bool) ![]const u8 { if (!is_colorized) { return try std.fmt.bufPrint( buf, "{}", .{std.fmt.fmtDurationSigned(delta)}, ); } const color: std.io.tty.Color = if (delta < 1000000) .green else if (delta < 5000000) .yellow else .red; var duration_buf: [256]u8 = undefined; const formatted_duration = try std.fmt.bufPrint( &duration_buf, "{}", .{std.fmt.fmtDurationSigned(delta)}, ); return try colorize(color, buf, formatted_duration, true); }