const std = @import("std");

pub const Span = @import("span.zig");
pub const Unicode = @import("unicode.zig");
pub const json_string = @import("json_string.zig");
pub const ascii = @import("ascii.zig");
pub const datetime = @import("datetime.zig");
pub const width = @import("width.zig");

test {
    _ = json_string;
    _ = ascii;
    _ = datetime;
    _ = Unicode;
    _ = width;
}

/// Like std.mem.eql, but also works if type has a `.eql` function defined.
pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
    if (a.len != b.len) return false;
    switch (@typeInfo(T)) {
        .@"struct", .@"union", .@"opaque", .vector, .array => {
            if (!@hasDecl(T, "eql")) @compileError("No eql defined for this type");
            for (a, b) |aa, bb| if (!aa.eql(bb)) return false;
        },
        else => for (a, b) |aa, bb| if (aa != bb) return false,
    }
    return true;
}

/// True when byte slice `s` exactly equals one of `options`. The shared form of
/// the keyword-set matching the format parsers do (null/bool spellings, the
/// `inf`/`nan` floats, etc.).
pub fn eqlAny(s: []const u8, options: []const []const u8) bool {
    for (options) |o| if (std.mem.eql(u8, s, o)) return true;
    return false;
}

test "util.eql" {
    try std.testing.expect(eql(u8, "hello, world!", "hello, world!"));
}

test "util.eqlAny" {
    try std.testing.expect(eqlAny("yes", &.{ "y", "yes", "true" }));
    try std.testing.expect(!eqlAny("maybe", &.{ "y", "yes", "true" }));
}
