Something went wrong. Try again.
window manager for the river wayland compositor
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109// zwanzig-disable: file-as-struct// Pure-function module keeps the PascalCase name per project convention.// SPDX-FileCopyrightText: © 2026 mtmn//// SPDX-License-Identifier: BSD-3-Clause
const std = @import("std");const mem = std.mem;
/// Validate a glob, returning error.InvalidGlob if it is empty, "**" or has a/// '*' at any position other than the first and/or last byte.pub fn validate(glob: []const u8) error{InvalidGlob}!void { switch (glob.len) { 0 => return error.InvalidGlob, 1 => {}, 2 => if (glob[0] == '*' and glob[1] == '*') return error.InvalidGlob, else => if (mem.indexOfScalar(u8, glob[1 .. glob.len - 1], '*') != null) { return error.InvalidGlob; }, }}
/// Return true if text is matched by glob. The glob must be valid,/// see validate().pub fn match(text: []const u8, glob: []const u8) bool { if (glob.len == 1) { return glob[0] == '*' or mem.eql(u8, text, glob); }
const suffix_match = glob[0] == '*'; const prefix_match = glob[glob.len - 1] == '*';
if (suffix_match and prefix_match) { return mem.indexOf(u8, text, glob[1 .. glob.len - 1]) != null; } else if (suffix_match) { return mem.endsWith(u8, text, glob[1..]); } else if (prefix_match) { return mem.startsWith(u8, text, glob[0 .. glob.len - 1]); } else { return mem.eql(u8, text, glob); }}
pub const Action = enum { float, fullscreen, csd, tags,};
pub const Rule = struct { app_id_glob: []const u8, title_glob: []const u8, action: Action, tags: u32 = 0,};
test validate { const testing = std.testing;
try validate("*"); try validate("a"); try validate("*a"); try validate("a*"); try validate("*a*"); try validate("ab");
try testing.expectError(error.InvalidGlob, validate("")); try testing.expectError(error.InvalidGlob, validate("**")); try testing.expectError(error.InvalidGlob, validate("***")); try testing.expectError(error.InvalidGlob, validate("a*c")); try testing.expectError(error.InvalidGlob, validate("ab*c*")); try testing.expectError(error.InvalidGlob, validate("*ab*c")); try testing.expectError(error.InvalidGlob, validate("**a")); try testing.expectError(error.InvalidGlob, validate("abc**"));}
test match { const testing = std.testing;
try testing.expect(match("", "*")); try testing.expect(match("a", "*")); try testing.expect(match("a", "*a*")); try testing.expect(match("a", "a*")); try testing.expect(match("a", "*a")); try testing.expect(match("a", "a"));
try testing.expect(!match("a", "b")); try testing.expect(!match("a", "*b*")); try testing.expect(!match("a", "b*")); try testing.expect(!match("a", "*b"));
try testing.expect(match("ab", "*ab*")); try testing.expect(match("ab", "*b")); try testing.expect(match("ab", "a*"));
try testing.expect(!match("ab", "b*")); try testing.expect(!match("ab", "*a")); try testing.expect(!match("ab", "ac")); try testing.expect(!match("ab", "*ac*"));}
test "rule matches app id and title together" { const testing = std.testing; try testing.expect(match("float-term", "float*")); try testing.expect(!match("other", "float*")); try testing.expect(match("foo", "*"));}