Create config/helpers.zig
This moves all the helper functions from Config.zig into their own file
This commit is contained in:
parent
c1c9eb24f7
commit
8b43e491e7
2 changed files with 176 additions and 155 deletions
183
src/Config.zig
183
src/Config.zig
|
|
@ -320,7 +320,7 @@ fn load(config: *Config, reader: *Io.Reader) !void {
|
||||||
// If it's a node, we check if it's a valid NodeName
|
// If it's a node, we check if it's a valid NodeName
|
||||||
const node_name = std.meta.stringToEnum(NodeName, node.name);
|
const node_name = std.meta.stringToEnum(NodeName, node.name);
|
||||||
if (node_name) |name| {
|
if (node_name) |name| {
|
||||||
if (!hostMatches(node, &parser, hostname)) {
|
if (!helpers.hostMatches(node, &parser, hostname)) {
|
||||||
logDebugHostMismatch(name);
|
logDebugHostMismatch(name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -356,7 +356,7 @@ fn load(config: *Config, reader: *Io.Reader) !void {
|
||||||
},
|
},
|
||||||
.focus_follows_pointer => {
|
.focus_follows_pointer => {
|
||||||
const focus_follows_pointer_str = utils.stripQuotes(node.arg(&parser, 0) orelse "");
|
const focus_follows_pointer_str = utils.stripQuotes(node.arg(&parser, 0) orelse "");
|
||||||
if (boolFromKdlStr(focus_follows_pointer_str)) |focus_follows_pointer| {
|
if (helpers.boolFromKdlStr(focus_follows_pointer_str)) |focus_follows_pointer| {
|
||||||
config.focus_follows_pointer = focus_follows_pointer;
|
config.focus_follows_pointer = focus_follows_pointer;
|
||||||
logDebugSettingNode(name, focus_follows_pointer_str);
|
logDebugSettingNode(name, focus_follows_pointer_str);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -366,7 +366,7 @@ fn load(config: *Config, reader: *Io.Reader) !void {
|
||||||
},
|
},
|
||||||
.pointer_warp_on_focus_change => {
|
.pointer_warp_on_focus_change => {
|
||||||
const pointer_warp_on_focus_change_str = utils.stripQuotes(node.arg(&parser, 0) orelse "");
|
const pointer_warp_on_focus_change_str = utils.stripQuotes(node.arg(&parser, 0) orelse "");
|
||||||
if (boolFromKdlStr(pointer_warp_on_focus_change_str)) |pointer_warp_on_focus_change| {
|
if (helpers.boolFromKdlStr(pointer_warp_on_focus_change_str)) |pointer_warp_on_focus_change| {
|
||||||
config.pointer_warp_on_focus_change = pointer_warp_on_focus_change;
|
config.pointer_warp_on_focus_change = pointer_warp_on_focus_change;
|
||||||
logDebugSettingNode(name, pointer_warp_on_focus_change_str);
|
logDebugSettingNode(name, pointer_warp_on_focus_change_str);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -381,7 +381,7 @@ fn load(config: *Config, reader: *Io.Reader) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
const path_str = utils.stripQuotes(node.arg(&parser, 0).?);
|
const path_str = utils.stripQuotes(node.arg(&parser, 0).?);
|
||||||
config.wallpaper_image_path = expandTilde(path_str) catch {
|
config.wallpaper_image_path = helpers.expandTilde(path_str) catch {
|
||||||
logWarnInvalidNodeArg(name, path_str);
|
logWarnInvalidNodeArg(name, path_str);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
@ -429,7 +429,7 @@ fn load(config: *Config, reader: *Io.Reader) !void {
|
||||||
}
|
}
|
||||||
next_child_block = null;
|
next_child_block = null;
|
||||||
} else {
|
} else {
|
||||||
try config.skipChildBlock(&parser);
|
try helpers.skipChildBlock(&parser);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.child_block_end => log.err("Reached unexpected .child_block_end. Ignoring it", .{}),
|
.child_block_end => log.err("Reached unexpected .child_block_end. Ignoring it", .{}),
|
||||||
|
|
@ -444,7 +444,7 @@ fn loadBordersChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]cons
|
||||||
// If it's a node, we check if it's a valid NodeName
|
// If it's a node, we check if it's a valid NodeName
|
||||||
const node_name = std.meta.stringToEnum(BorderNodeName, node.name);
|
const node_name = std.meta.stringToEnum(BorderNodeName, node.name);
|
||||||
if (node_name) |name| {
|
if (node_name) |name| {
|
||||||
if (!hostMatches(node, parser, hostname)) {
|
if (!helpers.hostMatches(node, parser, hostname)) {
|
||||||
logDebugHostMismatch(name);
|
logDebugHostMismatch(name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -480,7 +480,7 @@ fn loadBordersChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]cons
|
||||||
},
|
},
|
||||||
.child_block_begin => {
|
.child_block_begin => {
|
||||||
// borders should never have a nested child block
|
// borders should never have a nested child block
|
||||||
try config.skipChildBlock(parser);
|
try helpers.skipChildBlock(parser);
|
||||||
},
|
},
|
||||||
.child_block_end => {
|
.child_block_end => {
|
||||||
// Done parsing the borders block; return
|
// Done parsing the borders block; return
|
||||||
|
|
@ -505,7 +505,7 @@ fn loadTagOverlayChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]c
|
||||||
}
|
}
|
||||||
const node_name = std.meta.stringToEnum(TagOverlayNodeName, node.name);
|
const node_name = std.meta.stringToEnum(TagOverlayNodeName, node.name);
|
||||||
if (node_name) |name| {
|
if (node_name) |name| {
|
||||||
if (!hostMatches(node, parser, hostname)) {
|
if (!helpers.hostMatches(node, parser, hostname)) {
|
||||||
logDebugHostMismatch(name);
|
logDebugHostMismatch(name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -564,7 +564,7 @@ fn loadTagOverlayChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]c
|
||||||
}
|
}
|
||||||
next_child_block = null;
|
next_child_block = null;
|
||||||
} else {
|
} else {
|
||||||
try config.skipChildBlock(parser);
|
try helpers.skipChildBlock(parser);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.child_block_end => return,
|
.child_block_end => return,
|
||||||
|
|
@ -578,12 +578,12 @@ fn loadTagOverlayAnchorsBlock(config: *Config, parser: *kdl.Parser, hostname: ?[
|
||||||
.node => |node| {
|
.node => |node| {
|
||||||
const node_name = std.meta.stringToEnum(TagOverlayAnchorsNodeName, node.name);
|
const node_name = std.meta.stringToEnum(TagOverlayAnchorsNodeName, node.name);
|
||||||
if (node_name) |name| {
|
if (node_name) |name| {
|
||||||
if (!hostMatches(node, parser, hostname)) {
|
if (!helpers.hostMatches(node, parser, hostname)) {
|
||||||
logDebugHostMismatch(name);
|
logDebugHostMismatch(name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse "");
|
const val_str = utils.stripQuotes(node.arg(parser, 0) orelse "");
|
||||||
if (boolFromKdlStr(val_str)) |val| {
|
if (helpers.boolFromKdlStr(val_str)) |val| {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
.top => config.tag_overlay.?.anchor_top = val,
|
.top => config.tag_overlay.?.anchor_top = val,
|
||||||
.right => config.tag_overlay.?.anchor_right = val,
|
.right => config.tag_overlay.?.anchor_right = val,
|
||||||
|
|
@ -598,7 +598,7 @@ fn loadTagOverlayAnchorsBlock(config: *Config, parser: *kdl.Parser, hostname: ?[
|
||||||
logWarnInvalidNode(node.name);
|
logWarnInvalidNode(node.name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.child_block_begin => try config.skipChildBlock(parser),
|
.child_block_begin => try helpers.skipChildBlock(parser),
|
||||||
.child_block_end => return,
|
.child_block_end => return,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -610,7 +610,7 @@ fn loadTagOverlayMarginsBlock(config: *Config, parser: *kdl.Parser, hostname: ?[
|
||||||
.node => |node| {
|
.node => |node| {
|
||||||
const node_name = std.meta.stringToEnum(TagOverlayMarginsNodeName, node.name);
|
const node_name = std.meta.stringToEnum(TagOverlayMarginsNodeName, node.name);
|
||||||
if (node_name) |name| {
|
if (node_name) |name| {
|
||||||
if (!hostMatches(node, parser, hostname)) {
|
if (!helpers.hostMatches(node, parser, hostname)) {
|
||||||
logDebugHostMismatch(name);
|
logDebugHostMismatch(name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -630,7 +630,7 @@ fn loadTagOverlayMarginsBlock(config: *Config, parser: *kdl.Parser, hostname: ?[
|
||||||
logWarnInvalidNode(node.name);
|
logWarnInvalidNode(node.name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.child_block_begin => try config.skipChildBlock(parser),
|
.child_block_begin => try helpers.skipChildBlock(parser),
|
||||||
.child_block_end => return,
|
.child_block_end => return,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -642,7 +642,7 @@ fn loadKeybindsChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]con
|
||||||
.node => |node| {
|
.node => |node| {
|
||||||
// tag_bind is a special case node name not in KeybindNodeName
|
// tag_bind is a special case node name not in KeybindNodeName
|
||||||
if (mem.eql(u8, node.name, "tag_bind")) {
|
if (mem.eql(u8, node.name, "tag_bind")) {
|
||||||
if (!hostMatches(node, parser, hostname)) {
|
if (!helpers.hostMatches(node, parser, hostname)) {
|
||||||
log.debug("Skipping \"keybind.tag_bind\" (host mismatch)", .{});
|
log.debug("Skipping \"keybind.tag_bind\" (host mismatch)", .{});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -687,7 +687,7 @@ fn loadKeybindsChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]con
|
||||||
// Handle the rest of the possibilities like all the other types of block
|
// Handle the rest of the possibilities like all the other types of block
|
||||||
const node_name = std.meta.stringToEnum(KeybindNodeName, node.name);
|
const node_name = std.meta.stringToEnum(KeybindNodeName, node.name);
|
||||||
if (node_name) |name| {
|
if (node_name) |name| {
|
||||||
if (!hostMatches(node, parser, hostname)) {
|
if (!helpers.hostMatches(node, parser, hostname)) {
|
||||||
logDebugHostMismatch(name);
|
logDebugHostMismatch(name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -721,7 +721,7 @@ fn loadKeybindsChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]con
|
||||||
var split_exec = try utils.tokenizeToOwnedSlices(exec_str, ' ');
|
var split_exec = try utils.tokenizeToOwnedSlices(exec_str, ' ');
|
||||||
if (split_exec.len > 0) {
|
if (split_exec.len > 0) {
|
||||||
// Expand ~ in executable paths
|
// Expand ~ in executable paths
|
||||||
const expanded = expandTilde(split_exec[0]) catch |e| {
|
const expanded = helpers.expandTilde(split_exec[0]) catch |e| {
|
||||||
if (e == error.HomeNotSet) {
|
if (e == error.HomeNotSet) {
|
||||||
// No ~, just return what we had.
|
// No ~, just return what we had.
|
||||||
break :sw .{ .spawn = split_exec };
|
break :sw .{ .spawn = split_exec };
|
||||||
|
|
@ -808,7 +808,7 @@ fn loadKeybindsChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[]con
|
||||||
},
|
},
|
||||||
.child_block_begin => {
|
.child_block_begin => {
|
||||||
// keybinds should never have a nested child block
|
// keybinds should never have a nested child block
|
||||||
try config.skipChildBlock(parser);
|
try helpers.skipChildBlock(parser);
|
||||||
},
|
},
|
||||||
.child_block_end => {
|
.child_block_end => {
|
||||||
// Done parsing the keybinds block; return
|
// Done parsing the keybinds block; return
|
||||||
|
|
@ -824,7 +824,7 @@ fn loadPointerBindsChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[
|
||||||
.node => |node| {
|
.node => |node| {
|
||||||
const node_name = std.meta.stringToEnum(PointerBindNodeName, node.name);
|
const node_name = std.meta.stringToEnum(PointerBindNodeName, node.name);
|
||||||
if (node_name) |name| {
|
if (node_name) |name| {
|
||||||
if (!hostMatches(node, parser, hostname)) {
|
if (!helpers.hostMatches(node, parser, hostname)) {
|
||||||
logDebugHostMismatch(name);
|
logDebugHostMismatch(name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -843,7 +843,7 @@ fn loadPointerBindsChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[
|
||||||
logWarnMissingNodeArg(name, "button");
|
logWarnMissingNodeArg(name, "button");
|
||||||
continue;
|
continue;
|
||||||
});
|
});
|
||||||
const button = parseButton(button_str) orelse {
|
const button = helpers.parseButton(button_str) orelse {
|
||||||
logWarnInvalidNodeArg(name, button_str);
|
logWarnInvalidNodeArg(name, button_str);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
@ -865,7 +865,7 @@ fn loadPointerBindsChildBlock(config: *Config, parser: *kdl.Parser, hostname: ?[
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.child_block_begin => {
|
.child_block_begin => {
|
||||||
try config.skipChildBlock(parser);
|
try helpers.skipChildBlock(parser);
|
||||||
},
|
},
|
||||||
.child_block_end => {
|
.child_block_end => {
|
||||||
return;
|
return;
|
||||||
|
|
@ -883,7 +883,7 @@ fn loadInputChildBlock(config: *Config, parser: *kdl.Parser, name: ?[]const u8,
|
||||||
.node => |node| {
|
.node => |node| {
|
||||||
const node_name = std.meta.stringToEnum(InputConfigNodeName, node.name);
|
const node_name = std.meta.stringToEnum(InputConfigNodeName, node.name);
|
||||||
if (node_name) |tag| {
|
if (node_name) |tag| {
|
||||||
if (!hostMatches(node, parser, hostname)) {
|
if (!helpers.hostMatches(node, parser, hostname)) {
|
||||||
logDebugHostMismatch(tag);
|
logDebugHostMismatch(tag);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -901,7 +901,7 @@ fn loadInputChildBlock(config: *Config, parser: *kdl.Parser, name: ?[]const u8,
|
||||||
log.debug("input.accel_speed: {s}", .{val_str});
|
log.debug("input.accel_speed: {s}", .{val_str});
|
||||||
},
|
},
|
||||||
.scroll_button => {
|
.scroll_button => {
|
||||||
const button = parseButton(val_str) orelse {
|
const button = helpers.parseButton(val_str) orelse {
|
||||||
logWarnInvalidNodeArg(tag, val_str);
|
logWarnInvalidNodeArg(tag, val_str);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
@ -951,7 +951,7 @@ fn loadInputChildBlock(config: *Config, parser: *kdl.Parser, name: ?[]const u8,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.child_block_begin => {
|
.child_block_begin => {
|
||||||
try config.skipChildBlock(parser);
|
try helpers.skipChildBlock(parser);
|
||||||
},
|
},
|
||||||
.child_block_end => {
|
.child_block_end => {
|
||||||
try config.input_configs.append(utils.gpa, input_config);
|
try config.input_configs.append(utils.gpa, input_config);
|
||||||
|
|
@ -961,65 +961,6 @@ fn loadInputChildBlock(config: *Config, parser: *kdl.Parser, name: ?[]const u8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseButton(s: []const u8) ?u32 {
|
|
||||||
// Support both numeric and named buttons
|
|
||||||
var lower_buf: [16]u8 = undefined;
|
|
||||||
const len = @min(s.len, 16);
|
|
||||||
const lower = std.ascii.lowerString(lower_buf[0..len], s[0..len]);
|
|
||||||
|
|
||||||
if (mem.eql(u8, lower, "btn_left") or mem.eql(u8, lower, "button1")) {
|
|
||||||
return 0x110; // BTN_LEFT = 272
|
|
||||||
} else if (mem.eql(u8, lower, "btn_right") or mem.eql(u8, lower, "button3")) {
|
|
||||||
return 0x111; // BTN_RIGHT = 273
|
|
||||||
} else if (mem.eql(u8, lower, "btn_middle") or mem.eql(u8, lower, "button2")) {
|
|
||||||
return 0x112; // BTN_MIDDLE = 274
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try parsing as hex or decimal
|
|
||||||
return fmt.parseInt(u32, s, 0) catch null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Skips an entire child block including any nested child blocks
|
|
||||||
fn skipChildBlock(_: *Config, parser: *kdl.Parser) !void {
|
|
||||||
log.warn("Unexpected child block. Skipping it", .{});
|
|
||||||
|
|
||||||
var depth: usize = 0;
|
|
||||||
while (try parser.next()) |event| {
|
|
||||||
switch (event) {
|
|
||||||
// Nested child block
|
|
||||||
.child_block_begin => depth += 1,
|
|
||||||
.child_block_end => {
|
|
||||||
if (depth == 0) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
depth -= 1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
// We don't care about any nodes in here
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert a KDL argument into a bool
|
|
||||||
///
|
|
||||||
/// if arg_str in ["#true", "true"], return true
|
|
||||||
/// if arg_str in ["#false", "false"], return false
|
|
||||||
/// else, return null
|
|
||||||
fn boolFromKdlStr(arg_str: []const u8) ?bool {
|
|
||||||
if (mem.eql(u8, arg_str, "#true") or
|
|
||||||
mem.eql(u8, arg_str, "true"))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
} else if (mem.eql(u8, arg_str, "#false") or
|
|
||||||
mem.eql(u8, arg_str, "false"))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn logWarnInvalidNodeArg(node_name: anytype, node_value: []const u8) void {
|
fn logWarnInvalidNodeArg(node_name: anytype, node_value: []const u8) void {
|
||||||
const node_name_type = @TypeOf(node_name);
|
const node_name_type = @TypeOf(node_name);
|
||||||
switch (node_name_type) {
|
switch (node_name_type) {
|
||||||
|
|
@ -1086,23 +1027,6 @@ fn logDebugSettingNode(node_name: anytype, node_value: []const u8) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expandTilde(path: []const u8) ![]const u8 {
|
|
||||||
if (path.len > 0 and path[0] == '~') {
|
|
||||||
const home = std.posix.getenv("HOME") orelse return error.HomeNotSet;
|
|
||||||
return std.fmt.allocPrint(utils.gpa, "{s}{s}", .{ home, path[1..] });
|
|
||||||
}
|
|
||||||
return utils.gpa.dupe(u8, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check whether this machine's hostname matches the hostname property
|
|
||||||
/// Always returns true if the "host" property is missing (no host = config applies to
|
|
||||||
/// all hosts). Returns false if the hostname argument is null or does not match.
|
|
||||||
fn hostMatches(node: kdl.Parser.Node, parser: *kdl.Parser, hostname: ?[]const u8) bool {
|
|
||||||
const host_property = utils.stripQuotes(node.prop(parser, "host") orelse return true);
|
|
||||||
const hostname_str = hostname orelse return false;
|
|
||||||
return mem.eql(u8, host_property, hostname_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const fmt = std.fmt;
|
const fmt = std.fmt;
|
||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
|
|
@ -1138,63 +1062,12 @@ const RiverColor = utils.RiverColor;
|
||||||
const TagOverlay = @import("TagOverlay.zig");
|
const TagOverlay = @import("TagOverlay.zig");
|
||||||
const XkbBindings = @import("XkbBindings.zig");
|
const XkbBindings = @import("XkbBindings.zig");
|
||||||
|
|
||||||
|
const helpers = @import("config/helpers.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.Config);
|
const log = std.log.scoped(.Config);
|
||||||
|
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
test "boolFromKdlStr" {
|
test {
|
||||||
// True valid
|
_ = helpers;
|
||||||
try testing.expectEqual(@as(?bool, true), boolFromKdlStr("#true"));
|
|
||||||
try testing.expectEqual(@as(?bool, true), boolFromKdlStr("true"));
|
|
||||||
// False valid
|
|
||||||
try testing.expectEqual(@as(?bool, false), boolFromKdlStr("#false"));
|
|
||||||
try testing.expectEqual(@as(?bool, false), boolFromKdlStr("false"));
|
|
||||||
// Invalid
|
|
||||||
try testing.expectEqual(@as(?bool, null), boolFromKdlStr("yes"));
|
|
||||||
try testing.expectEqual(@as(?bool, null), boolFromKdlStr("1"));
|
|
||||||
try testing.expectEqual(@as(?bool, null), boolFromKdlStr(""));
|
|
||||||
try testing.expectEqual(@as(?bool, null), boolFromKdlStr("TRUE"));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "parseButton named buttons" {
|
|
||||||
try testing.expectEqual(@as(?u32, 0x110), parseButton("btn_left"));
|
|
||||||
try testing.expectEqual(@as(?u32, 0x110), parseButton("button1"));
|
|
||||||
try testing.expectEqual(@as(?u32, 0x111), parseButton("btn_right"));
|
|
||||||
try testing.expectEqual(@as(?u32, 0x111), parseButton("button3"));
|
|
||||||
try testing.expectEqual(@as(?u32, 0x112), parseButton("btn_middle"));
|
|
||||||
try testing.expectEqual(@as(?u32, 0x112), parseButton("button2"));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "parseButton case insensitive" {
|
|
||||||
try testing.expectEqual(@as(?u32, 0x110), parseButton("BTN_LEFT"));
|
|
||||||
try testing.expectEqual(@as(?u32, 0x110), parseButton("Btn_Left"));
|
|
||||||
try testing.expectEqual(@as(?u32, 0x110), parseButton("BUTTON1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "parseButton numeric decimal" {
|
|
||||||
try testing.expectEqual(@as(?u32, 272), parseButton("272"));
|
|
||||||
try testing.expectEqual(@as(?u32, 0), parseButton("0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "parseButton numeric hex" {
|
|
||||||
try testing.expectEqual(@as(?u32, 0x110), parseButton("0x110"));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "parseButton invalid" {
|
|
||||||
try testing.expectEqual(@as(?u32, null), parseButton("bogus"));
|
|
||||||
try testing.expectEqual(@as(?u32, null), parseButton(""));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "expandTilde with tilde" {
|
|
||||||
const result = try expandTilde("~/foo/bar");
|
|
||||||
defer utils.gpa.free(result);
|
|
||||||
const home = std.posix.getenv("HOME") orelse return;
|
|
||||||
try testing.expect(mem.startsWith(u8, result, home));
|
|
||||||
try testing.expect(mem.endsWith(u8, result, "/foo/bar"));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "expandTilde without tilde" {
|
|
||||||
const result = try expandTilde("/absolute/path");
|
|
||||||
defer utils.gpa.free(result);
|
|
||||||
try testing.expectEqualStrings("/absolute/path", result);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
148
src/config/helpers.zig
Normal file
148
src/config/helpers.zig
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
// SPDX-FileCopyrightText: 2026 Ben Buhse <me@benbuhse.email>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
/// Convert a KDL argument into a bool
|
||||||
|
///
|
||||||
|
/// if arg_str in ["#true", "true"], return true
|
||||||
|
/// if arg_str in ["#false", "false"], return false
|
||||||
|
/// else, return null
|
||||||
|
pub fn boolFromKdlStr(arg_str: []const u8) ?bool {
|
||||||
|
if (mem.eql(u8, arg_str, "#true") or
|
||||||
|
mem.eql(u8, arg_str, "true"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
} else if (mem.eql(u8, arg_str, "#false") or
|
||||||
|
mem.eql(u8, arg_str, "false"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parseButton(s: []const u8) ?u32 {
|
||||||
|
// Support both numeric and named buttons
|
||||||
|
var lower_buf: [16]u8 = undefined;
|
||||||
|
const len = @min(s.len, 16);
|
||||||
|
const lower = std.ascii.lowerString(lower_buf[0..len], s[0..len]);
|
||||||
|
|
||||||
|
if (mem.eql(u8, lower, "btn_left") or mem.eql(u8, lower, "button1")) {
|
||||||
|
return 0x110; // BTN_LEFT = 272
|
||||||
|
} else if (mem.eql(u8, lower, "btn_right") or mem.eql(u8, lower, "button3")) {
|
||||||
|
return 0x111; // BTN_RIGHT = 273
|
||||||
|
} else if (mem.eql(u8, lower, "btn_middle") or mem.eql(u8, lower, "button2")) {
|
||||||
|
return 0x112; // BTN_MIDDLE = 274
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try parsing as hex or decimal
|
||||||
|
return fmt.parseInt(u32, s, 0) catch null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expandTilde(path: []const u8) ![]const u8 {
|
||||||
|
if (path.len > 0 and path[0] == '~') {
|
||||||
|
const home = std.posix.getenv("HOME") orelse return error.HomeNotSet;
|
||||||
|
return std.fmt.allocPrint(utils.gpa, "{s}{s}", .{ home, path[1..] });
|
||||||
|
}
|
||||||
|
return utils.gpa.dupe(u8, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether this machine's hostname matches the hostname property
|
||||||
|
/// Always returns true if the "host" property is missing (no host = config applies to
|
||||||
|
/// all hosts). Returns false if the hostname argument is null or does not match.
|
||||||
|
pub fn hostMatches(node: kdl.Parser.Node, parser: *kdl.Parser, hostname: ?[]const u8) bool {
|
||||||
|
const host_property = utils.stripQuotes(node.prop(parser, "host") orelse return true);
|
||||||
|
const hostname_str = hostname orelse return false;
|
||||||
|
return mem.eql(u8, host_property, hostname_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Skips an entire child block including any nested child blocks
|
||||||
|
pub fn skipChildBlock(parser: *kdl.Parser) !void {
|
||||||
|
log.warn("Unexpected child block. Skipping it", .{});
|
||||||
|
|
||||||
|
var depth: usize = 0;
|
||||||
|
while (try parser.next()) |event| {
|
||||||
|
switch (event) {
|
||||||
|
// Nested child block
|
||||||
|
.child_block_begin => depth += 1,
|
||||||
|
.child_block_end => {
|
||||||
|
if (depth == 0) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
depth -= 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
// We don't care about any nodes in here
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const fmt = std.fmt;
|
||||||
|
const mem = std.mem;
|
||||||
|
|
||||||
|
const kdl = @import("kdl");
|
||||||
|
|
||||||
|
const utils = @import("../utils.zig");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.config_helpers);
|
||||||
|
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
test "boolFromKdlStr" {
|
||||||
|
// True valid
|
||||||
|
try testing.expectEqual(@as(?bool, true), boolFromKdlStr("#true"));
|
||||||
|
try testing.expectEqual(@as(?bool, true), boolFromKdlStr("true"));
|
||||||
|
// False valid
|
||||||
|
try testing.expectEqual(@as(?bool, false), boolFromKdlStr("#false"));
|
||||||
|
try testing.expectEqual(@as(?bool, false), boolFromKdlStr("false"));
|
||||||
|
// Invalid
|
||||||
|
try testing.expectEqual(@as(?bool, null), boolFromKdlStr("yes"));
|
||||||
|
try testing.expectEqual(@as(?bool, null), boolFromKdlStr("1"));
|
||||||
|
try testing.expectEqual(@as(?bool, null), boolFromKdlStr(""));
|
||||||
|
try testing.expectEqual(@as(?bool, null), boolFromKdlStr("TRUE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parseButton named buttons" {
|
||||||
|
try testing.expectEqual(@as(?u32, 0x110), parseButton("btn_left"));
|
||||||
|
try testing.expectEqual(@as(?u32, 0x110), parseButton("button1"));
|
||||||
|
try testing.expectEqual(@as(?u32, 0x111), parseButton("btn_right"));
|
||||||
|
try testing.expectEqual(@as(?u32, 0x111), parseButton("button3"));
|
||||||
|
try testing.expectEqual(@as(?u32, 0x112), parseButton("btn_middle"));
|
||||||
|
try testing.expectEqual(@as(?u32, 0x112), parseButton("button2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parseButton case insensitive" {
|
||||||
|
try testing.expectEqual(@as(?u32, 0x110), parseButton("BTN_LEFT"));
|
||||||
|
try testing.expectEqual(@as(?u32, 0x110), parseButton("Btn_Left"));
|
||||||
|
try testing.expectEqual(@as(?u32, 0x110), parseButton("BUTTON1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parseButton numeric decimal" {
|
||||||
|
try testing.expectEqual(@as(?u32, 272), parseButton("272"));
|
||||||
|
try testing.expectEqual(@as(?u32, 0), parseButton("0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parseButton numeric hex" {
|
||||||
|
try testing.expectEqual(@as(?u32, 0x110), parseButton("0x110"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parseButton invalid" {
|
||||||
|
try testing.expectEqual(@as(?u32, null), parseButton("bogus"));
|
||||||
|
try testing.expectEqual(@as(?u32, null), parseButton(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "expandTilde with tilde" {
|
||||||
|
const result = try expandTilde("~/foo/bar");
|
||||||
|
defer utils.gpa.free(result);
|
||||||
|
const home = std.posix.getenv("HOME") orelse return;
|
||||||
|
try testing.expect(mem.startsWith(u8, result, home));
|
||||||
|
try testing.expect(mem.endsWith(u8, result, "/foo/bar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "expandTilde without tilde" {
|
||||||
|
const result = try expandTilde("/absolute/path");
|
||||||
|
defer utils.gpa.free(result);
|
||||||
|
try testing.expectEqualStrings("/absolute/path", result);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue