diff --git a/src/args/args.zig b/src/args/args.zig index ab037ab..c364252 100644 --- a/src/args/args.zig +++ b/src/args/args.zig @@ -2,7 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); -const inDebugMode = builtin.mode == .Debug; +const DEBUG_MODE = builtin.mode == .Debug; const Allocator = std.mem.Allocator; @@ -17,21 +17,21 @@ const log = std.log.scoped(.args); pub const Extra = union(enum(u2)) { Positional: struct { about: ?[]const u8 = null, - typeHint: ?[]const u8 = null, + type_hint: ?[]const u8 = null, }, Remainder, Flag: struct { name: []const u8, short: ?[]const u8 = null, about: ?[]const u8 = null, - typeHint: ?[]const u8 = null, + type_hint: ?[]const u8 = null, toggle: bool = false, - takesValue: bool = false, + takes_value: bool = false, /// Ensures that if the flag is present, the parser will not continue parsing. /// /// Warning: this is a dangerous option, as all subsequent fields will be ignored even if they have values. /// Only use this for flags like `--help` or `--version` or similar. - shortCircuit: bool = false, + short_circuit: bool = false, }, }; @@ -167,12 +167,12 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T { } const fie = &@field(result, field.name); - const fieldType = @typeInfo(@TypeOf(fie.*.value)); + const field_type = @typeInfo(@TypeOf(fie.*.value)); const extra: Extra = fie.*.extra; switch (extra) { .Flag => |f| { - if (!(f.takesValue or f.toggle)) { + if (!(f.takes_value or f.toggle)) { log.err("flag `{s}` must be a toggle or take a value, but it was neither", .{f.name}); unreachable; } @@ -185,7 +185,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T { if (@TypeOf(fie.*.value) == bool) { fie.*.value = true; - if (f.shortCircuit) { + if (f.short_circuit) { return result; } comptime continue; @@ -198,12 +198,12 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T { } } - if (f.takesValue) { + if (f.takes_value) { if (flag.*.Flag.value) |value| { if (@TypeOf(fie.*.value) == []const u8) { fie.*.value = try result.allocator.dupe(u8, value); - if (f.shortCircuit) { + if (f.short_circuit) { return result; } comptime continue; @@ -219,7 +219,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T { return error.InvalidFlag; }; - if (f.shortCircuit) { + if (f.short_circuit) { return result; } comptime continue; @@ -241,7 +241,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T { return error.InvalidFlag; } - switch (fieldType) { + switch (field_type) { .optional => |_| { log.debug("flag `{s}` is optional, and we couldn't find a value for it, so leaving as default value", .{f.name}); comptime continue; @@ -266,7 +266,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T { return error.NoValueForFlag; } - if (inDebugMode) { + if (DEBUG_MODE) { log.warn("flag `{s}` expected a value, but none was provided", .{f.name}); log.warn("expected type: {s}", .{niceTypeName(@TypeOf(fie.*.value))}); } else { @@ -355,7 +355,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T { return error.InvalidPositional; } } else { - switch (fieldType) { + switch (field_type) { .optional => { if (!builtin.is_test) { log.warn("could not find positional argument for `{s}`, using default value", .{field.name}); @@ -371,9 +371,6 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T { return error.NoArgumentFound; } - log.err("could not find positional argument for `{s}`", .{field.name}); - log.warn("hint: expected type: {s}", .{niceTypeName(@TypeOf(fie.*.value))}); - log.warn("hint: try ``", .{}); return error.NoArgumentFound; } }, @@ -396,7 +393,7 @@ test "parse args" { .extra = Extra{ .Flag = .{ .name = "flag", - .takesValue = true, + .takes_value = true, }, }, }, @@ -448,7 +445,7 @@ test "missing flag" { .extra = Extra{ .Flag = .{ .name = "flag", - .takesValue = true, + .takes_value = true, }, }, .parse = parsers.boolean, @@ -551,7 +548,7 @@ test "positional has default value so we get a free pass" { .extra = .{ .Positional = .{}, }, - .parse = parsers.numNullable(u8), + .parse = parsers.num_nullable(u8), }, fn deinit(self: *@This()) void { @@ -598,7 +595,7 @@ test "parse fn (positional)" { .extra = Extra{ .Positional = .{}, }, - .parse = parsers.enumLiteral(DemoEnum), + .parse = parsers.enum_literal(DemoEnum), }, fn deinit(self: *@This()) void { @@ -634,7 +631,7 @@ test "parse fn (flag)" { .extra = Extra{ .Flag = .{ .name = "number", - .takesValue = true, + .takes_value = true, }, }, .parse = parsers.num(u16), @@ -654,10 +651,10 @@ test "parse fn (flag)" { .extra = Extra{ .Flag = .{ .name = "enumeration", - .takesValue = true, + .takes_value = true, }, }, - .parse = parsers.enumLiteral(DemoEnum), + .parse = parsers.enum_literal(DemoEnum), }, fn deinit(self: *@This()) void { @@ -769,7 +766,7 @@ test "sub command from remainder" { .extra = .{ .Flag = .{ .name = "flag", - .takesValue = true, + .takes_value = true, }, }, }, diff --git a/src/args/help.zig b/src/args/help.zig index cea935d..5cd4446 100644 --- a/src/args/help.zig +++ b/src/args/help.zig @@ -2,9 +2,9 @@ const std = @import("std"); const log = std.log.scoped(.help); -const argLib = @import("args.zig"); -const Marker = argLib.Marker; -const Extra = argLib.Extra; +const arg_lib = @import("args.zig"); +const Marker = arg_lib.Marker; +const Extra = arg_lib.Extra; const niceTypeName = @import("../util/utils.zig").niceTypeName; diff --git a/src/args/parsers/enumLiteral.zig b/src/args/parsers/enumLiteral.zig index 1db737f..5be3166 100644 --- a/src/args/parsers/enumLiteral.zig +++ b/src/args/parsers/enumLiteral.zig @@ -14,9 +14,9 @@ pub fn enumLiteral(comptime T: type) *const fn (value: []const u8) anyerror!T { const log = std.log.scoped(.args); fn parseEnumFromStr(comptime T: type, str: []const u8) !T { - const heapAllocator = std.heap.page_allocator; - const lower = try std.ascii.allocLowerString(heapAllocator, str); - defer heapAllocator.free(lower); + const heap_allocator = std.heap.page_allocator; + const lower = try std.ascii.allocLowerString(heap_allocator, str); + defer heap_allocator.free(lower); const info = @typeInfo(T); comptime { @@ -26,9 +26,9 @@ fn parseEnumFromStr(comptime T: type, str: []const u8) !T { } inline for (info.@"enum".fields) |field| { - const lowerFieldName = try std.ascii.allocLowerString(heapAllocator, field.name); - defer heapAllocator.free(lowerFieldName); - if (std.mem.eql(u8, lowerFieldName, lower)) { + const lower_field_name = try std.ascii.allocLowerString(heap_allocator, field.name); + defer heap_allocator.free(lower_field_name); + if (std.mem.eql(u8, lower_field_name, lower)) { return @enumFromInt(field.value); } } @@ -42,9 +42,9 @@ fn parseEnumFromStr(comptime T: type, str: []const u8) !T { log.warn("hint: try one of the following:", .{}); inline for (info.@"enum".fields) |field| { - const lowerFieldName = try std.ascii.allocLowerString(heapAllocator, field.name); - defer heapAllocator.free(lowerFieldName); - log.warn("- {s}", .{lowerFieldName}); + const lower_field_name = try std.ascii.allocLowerString(heap_allocator, field.name); + defer heap_allocator.free(lower_field_name); + log.warn("- {s}", .{lower_field_name}); } return error.CouldNotParse; diff --git a/src/args/parsers/parsers.zig b/src/args/parsers/parsers.zig index 91e57d6..5c5a8a5 100644 --- a/src/args/parsers/parsers.zig +++ b/src/args/parsers/parsers.zig @@ -8,9 +8,9 @@ pub fn ParseSignature(comptime T: type) type { } pub const num = @import("./num.zig").num; -pub const numNullable = @import("./numNullable.zig").numNullable; +pub const num_nullable = @import("./numNullable.zig").numNullable; pub const boolean = @import("./boolean.zig").boolean; -pub const enumLiteral = @import("./enumLiteral.zig").enumLiteral; +pub const enum_literal = @import("./enumLiteral.zig").enumLiteral; test "valid parser signatures" { const t = std.testing; @@ -19,7 +19,7 @@ test "valid parser signatures" { try t.expectEqual(ParseSignature(bool), @TypeOf(&boolean)); const Enum = enum { Value, Other }; - try t.expectEqual(ParseSignature(Enum), @TypeOf(enumLiteral(Enum))); + try t.expectEqual(ParseSignature(Enum), @TypeOf(enum_literal(Enum))); } comptime { @@ -27,7 +27,8 @@ comptime { if (builtin.is_test) { std.mem.doNotOptimizeAway(num); + std.mem.doNotOptimizeAway(num_nullable); std.mem.doNotOptimizeAway(boolean); - std.mem.doNotOptimizeAway(enumLiteral); + std.mem.doNotOptimizeAway(enum_literal); } } diff --git a/src/log/logging.zig b/src/log/logging.zig index c2c31ae..880708b 100644 --- a/src/log/logging.zig +++ b/src/log/logging.zig @@ -21,8 +21,8 @@ pub const Color = enum { pub const ScopeModifier = struct { scope: SmartString, + prefix_match: bool = false, color: ?Color = .default, - bright: bool = false, rename: ?SmartString = null, }; @@ -31,13 +31,13 @@ const Allocator = std.mem.Allocator; const Globals = struct { allocator: Allocator, - enableFileOutput: bool = false, - outputFile: ?std.fs.File = null, + enable_file_output: bool = false, + output_file: ?std.fs.File = null, - additionalScopes: std.ArrayList(ScopeModifier), + additional_scopes: std.ArrayList(ScopeModifier), fn initOrGetFile(self: *Globals) !std.fs.File { - if (self.outputFile) |file| { + if (self.output_file) |file| { return file; } else { return error.NoFileSet; @@ -48,26 +48,26 @@ const Globals = struct { return .{ .allocator = allocator, - .enableFileOutput = false, - .outputFile = null, + .enable_file_output = false, + .output_file = null, - .additionalScopes = std.ArrayList(ScopeModifier).init(allocator), + .additional_scopes = std.ArrayList(ScopeModifier).init(allocator), }; } fn deinit(self: *Globals) void { - if (self.outputFile) |file| { + if (self.output_file) |file| { file.close(); } - for (self.additionalScopes.items) |*modifier| { + for (self.additional_scopes.items) |*modifier| { if (modifier.*.rename) |*value| { value.deinit(); } modifier.*.scope.deinit(); } - self.additionalScopes.deinit(); + self.additional_scopes.deinit(); self.* = undefined; } @@ -76,9 +76,9 @@ const Globals = struct { var core: ?Globals = null; pub const config = struct { - pub fn enableFileOutput(value: bool) void { + pub fn setFileOutput(value: bool) void { if (core) |*globals| { - globals.enableFileOutput = value; + globals.enable_file_output = value; } else { unreachable; // logging is not initialized } @@ -86,7 +86,7 @@ pub const config = struct { pub fn isFileOutput() bool { if (core) |*globals| { - return globals.enableFileOutput; + return globals.enable_file_output; } else { unreachable; // logging is not initialized } @@ -94,7 +94,7 @@ pub const config = struct { pub fn setOutputFile(file: std.fs.File) !void { if (core) |*globals| { - globals.outputFile = file; + globals.output_file = file; } else { unreachable; // logging is not initialized } @@ -102,7 +102,7 @@ pub const config = struct { pub fn getOutputFile() ?*const std.fs.File { if (core) |*globals| { - return &globals.outputFile; + return &globals.output_file; } else { unreachable; // logging is not initialized } @@ -110,7 +110,7 @@ pub const config = struct { pub fn addScope(modifier: ScopeModifier) !void { if (core) |*globals| { - try globals.additionalScopes.append(modifier); + try globals.additional_scopes.append(modifier); } else { unreachable; // logging is not initialized } @@ -146,6 +146,57 @@ fn get() *Globals { unreachable; // logging is not initialized } +fn isMatch(name: []const u8, modifier: *const ScopeModifier) bool { + if (modifier.prefix_match) { + return std.mem.startsWith(u8, name, modifier.scope.data); + } + + return std.mem.eql(u8, name, modifier.scope.data); +} + +fn prep(name: []const u8, modifier: ?*const ScopeModifier, allocator: Allocator) ![]const u8 { + if (!std.mem.containsAtLeastScalar(u8, name, 1, '_')) + return name; + + var c = cham.initRuntime(.{ + .allocator = allocator, + }); + + var chunks = std.mem.tokenizeScalar(u8, name, '_'); + + var output = std.ArrayList(u8).init(allocator); + + var isFirst = true; + while (chunks.next()) |chunk| { + if (!isFirst) { + _ = try output.writer().write("::"); + } else { + isFirst = false; + } + + if (modifier) |mod| { + if (mod.color) |color| { + _ = switch (color) { + .default => try output.writer().write(chunk), + .blue => try output.writer().write(try c.blue().fmt("{s}", .{chunk})), + .green => try output.writer().write(try c.green().fmt("{s}", .{chunk})), + .red => try output.writer().write(try c.red().fmt("{s}", .{chunk})), + .white => try output.writer().write(try c.white().fmt("{s}", .{chunk})), + .yellow => try output.writer().write(try c.yellow().fmt("{s}", .{chunk})), + .magenta => try output.writer().write(try c.magenta().fmt("{s}", .{chunk})), + .cyan => try output.writer().write(try c.cyan().fmt("{s}", .{chunk})), + }; + } else { + _ = try output.writer().write(chunk); + } + } else { + _ = try output.writer().write(chunk); + } + } + + return try output.toOwnedSlice(); +} + fn logFnImpl(comptime level: Level, comptime scope: Scope, comptime format: []const u8, args: anytype) !void { const globals = get(); var arena = std.heap.ArenaAllocator.init(globals.allocator); @@ -167,13 +218,13 @@ fn logFnImpl(comptime level: Level, comptime scope: Scope, comptime format: []co }, else => { - for (globals.additionalScopes.items) |modifier| { - if (std.mem.eql(u8, modifier.scope.data, @tagName(scope))) { + for (globals.additional_scopes.items) |modifier| { + if (isMatch(@tagName(scope), &modifier)) { const text = blk: { if (modifier.rename) |rename| { break :blk rename.data; } else { - break :blk @tagName(scope); + break :blk try prep(@tagName(scope), &modifier, arena.allocator()); } }; @@ -193,7 +244,7 @@ fn logFnImpl(comptime level: Level, comptime scope: Scope, comptime format: []co } } } else { - break :scopeTextBlk @tagName(scope); + break :scopeTextBlk try prep(@tagName(scope), null, arena.allocator()); } }, } @@ -215,7 +266,7 @@ fn logFnImpl(comptime level: Level, comptime scope: Scope, comptime format: []co const message = try std.fmt.allocPrint(arena.allocator(), format, args); - if (globals.enableFileOutput and globals.outputFile != null) { + if (globals.enable_file_output and globals.output_file != null) { var file = try globals.initOrGetFile(); var writer = file.writer().any(); @@ -238,17 +289,23 @@ test "logFn works" { defer deinit(); try config.addScope(.{ - .scope = SmartString.constant("someScope"), - .rename = SmartString.constant("some rename"), + .scope = .constant("some_scope"), + .rename = .constant("some rename"), .color = .green, }); try config.addScope(.{ - .scope = SmartString.constant("other"), - .rename = SmartString.constant("other rename"), + .scope = .constant("other"), + .rename = .constant("other rename"), .color = .blue, }); + try config.addScope(.{ + .scope = .constant("test"), + .prefix_match = true, + .color = .yellow, + }); try logFnImpl(.err, .default, "hello world", .{}); - try logFnImpl(.info, .someScope, "hello world", .{}); + try logFnImpl(.info, .some_scope, "hello world", .{}); try logFnImpl(.warn, .other, "hello world", .{}); + try logFnImpl(.err, .test_scope, "hello world", .{}); } diff --git a/src/util/SmartString.zig b/src/util/SmartString.zig index 7ac28d1..ac53597 100644 --- a/src/util/SmartString.zig +++ b/src/util/SmartString.zig @@ -205,20 +205,20 @@ const t = std.testing; test "the different kinds work" { const a = t.allocator; - var strOne = try SmartString.alloc("hello, world", a); - defer strOne.deinit(); + var str_one = try SmartString.alloc("hello, world", a); + defer str_one.deinit(); - try t.expectEqualStrings("hello, world", strOne.data); - try t.expectEqual(AllocKind{ .Allocated = a }, strOne.kind); + try t.expectEqualStrings("hello, world", str_one.data); + try t.expectEqual(AllocKind{ .Allocated = a }, str_one.kind); - var strTwo = SmartString.constant("hello, world"); - defer strTwo.deinit(); + var str_two = SmartString.constant("hello, world"); + defer str_two.deinit(); - try t.expectEqualStrings("hello, world", strTwo.data); - try t.expectEqual(AllocKind{ .Constant = {} }, strTwo.kind); + try t.expectEqualStrings("hello, world", str_two.data); + try t.expectEqual(AllocKind{ .Constant = {} }, str_two.kind); - try t.expectEqualStrings(strOne.data, strTwo.data); - try t.expect(!strOne.kind.eql(strTwo.kind)); + try t.expectEqualStrings(str_one.data, str_two.data); + try t.expect(!str_one.kind.eql(str_two.kind)); } test "allocKind eql works" { @@ -239,14 +239,14 @@ test "allocKind eql works" { test "clone works" { const a = t.allocator; - var strOne = try SmartString.alloc("hello, world", a); - defer strOne.deinit(); + var str_one = try SmartString.alloc("hello, world", a); + defer str_one.deinit(); - var strTwo = try strOne.clone(); - defer strTwo.deinit(); + var str_two = try str_one.clone(); + defer str_two.deinit(); - try t.expectEqualStrings("hello, world", strOne.data); - try t.expectEqualStrings("hello, world", strTwo.data); + try t.expectEqualStrings("hello, world", str_one.data); + try t.expectEqualStrings("hello, world", str_two.data); - try t.expect(strOne.kind.eql(strTwo.kind)); + try t.expect(str_one.kind.eql(str_two.kind)); }