I don’t understand why you return a slice, why not just return [3]u8
instead?
pub fn convertHexColorToRGB(comptime hex: []const u8) [3]u8 {
var result: [3]u8 = undefined;
for (0..3) |x| {
result[x] = hexStrToDec(hex[x * 2]) * 16 + hexStrToDec(hex[x * 2 + 1]);
}
return result;
}
Then the caller can use it more easily with comptime known length in the type and you don’t enter that whole uncertain area, of having to worry about where that temporary pointer may end up pointing and whether the memory it is pointing to is still correct or not.
Instead of using slices everywhere just use the same return type that comptimePrint
has *const [count(fmt, args):0]u8
const rgb_fmt = "{s}{d};2;{d};{d};{d}m";
fn getRGB(comptime mode: u8, r: u8, g: u8, b: u8) *const [std.fmt.count(rgb_fmt, .{ ESC, mode, r, g, b }):0]u8 {
return fmt(rgb_fmt, .{ ESC, mode, r, g, b });
}
fn getReset(comptime mode: u8) *const [std.fmt.count("{s}{d}m", .{ ESC, mode + 1 }):0]u8 {
return fmt("{s}{d}m", .{ ESC, mode + 1 });
}
Additionally I am not quite sure of your overall intent. For example why do you store your color as hex string and convert it, instead of using decimals to begin with? (maybe some other code requires that)
Mostly I find it difficult to tell what is part of the problem you are trying to solve and what might be things you don’t really need, but just things you have tried.
I think it would be good if you clarify your goal, inputs, outputs and whether those are runtime or comptime values.
While it is useful to do certain things at comptime, doing everything at comptime seems unpractical. I think things become way easier when you stop converting things to a string which you then print and instead use formatting functions that write to a writer directly.
While there are a lot of things that you can do at comptime, I think it is best to first do something simple and then figure out where comptime is really useful/beneficial.
So here is an example where I changed a bunch of stuff:
const std = @import("std");
pub const console = struct {
// https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences
// ansi escape codes for coloring text output
const ESC = "\u{001B}";
pub const RESET = ESC ++ "[0m";
pub const BLACK = ESC ++ "[1;30m";
pub const RED = ESC ++ "[1;31m";
pub const GREEN = ESC ++ "[1;32m";
pub const YELLOW = ESC ++ "[1;33m";
pub const BLUE = ESC ++ "[1;34m";
pub const MAGENTA = ESC ++ "[1;34m";
pub const CYAN = ESC ++ "[1;36m";
pub const WHITE = ESC ++ "[1;37m";
const rgb_format = ESC ++ "[38;2;{d};{d};{d}m";
fn rgb(args: anytype) *const [std.fmt.count(rgb_format, args):0]u8 {
if (comptime args.len != 3) @compileError("rgb expects a tuple of 3 arguments of u8");
return std.fmt.comptimePrint(rgb_format, args);
}
pub const add = rgb(.{ 120, 200, 120 }); // green
pub const remove = rgb(.{ 200, 120, 120 }); // red
pub const clear_line_right = ESC ++ "[0K";
pub const clear_line_left = ESC ++ "[1K";
pub const clear_line = ESC ++ "[2K";
pub const previous_line = ESC ++ "[F";
};
const Field = enum {
A,
B,
fn color(self: Field) std.fmt.Formatter(formatColor) {
return .{ .data = self };
}
fn formatColor(
self: Field,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
const c: [:0]const u8 = switch (self) {
.A => console.rgb(.{ 255, 0, 0 }),
.B => console.rgb(.{ 0, 0, 255 }),
};
try writer.print("{s}{s}{s}", .{ c, @tagName(self), console.RESET });
}
pub fn format(
self: Field,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
try writer.print("{s}", .{@tagName(self)});
}
};
const stdout = std.io.getStdOut().writer();
pub fn main() !void {
const field = Field.A;
try stdout.print("{}\n", .{field});
try stdout.print("{}\n", .{field.color()});
}
Also take a look at: Idiomatic Complex Formatting - #5 by castholm