typetui 0.2.0

A terminal-based typing test.
Documentation
const std = @import("std");

fn max(comptime T: type, a: T, b: T) T {
    return if (a > b) a else b;
}

test "comptime max" {
    const result_i32 = max(i32, 10, 20);
    const result_f64 = max(f64, 3.14, 2.71);

    try std.testing.expectEqual(result_i32, 20);
    try std.testing.expect(result_f64 == 3.14);
}

fn createArray(comptime T: type, comptime size: usize) [size]T {
    var arr: [size]T = undefined;
    for (0..size) |i| {
        arr[i] = @intCast(i);
    }
    return arr;
}

test "comptime array creation" {
    const arr = createArray(u8, 5);
    try std.testing.expectEqual(arr[0], 0);
    try std.testing.expectEqual(arr[4], 4);
}

fn Factorial(comptime n: u32) u32 {
    if (n == 0) return 1;
    return n * Factorial(n - 1);
}

test "comptime factorial" {
    try std.testing.expectEqual(Factorial(0), 1);
    try std.testing.expectEqual(Factorial(5), 120);
    try std.testing.expectEqual(Factorial(10), 3628800);
}

fn Fibonacci(comptime n: u32) u32 {
    if (n <= 1) return n;
    return Fibonacci(n - 1) + Fibonacci(n - 2);
}

test "comptime fibonacci" {
    try std.testing.expectEqual(Fibonacci(0), 0);
    try std.testing.expectEqual(Fibonacci(1), 1);
    try std.testing.expectEqual(Fibonacci(10), 55);
}

fn has_method(comptime T: type, comptime name: []const u8) bool {
    return @hasDecl(T, name);
}

const Person = struct {
    name: []const u8,
    age: u32,

    pub fn greet(self: @This()) void {
        std.debug.print("Hello, I'm {s}!\n", .{self.name});
    }

    pub fn get_age(self: @This()) u32 {
        return self.age;
    }
};

test "has_method check" {
    try std.testing.expect(has_method(Person, "greet"));
    try std.testing.expect(has_method(Person, "get_age"));
    try std.testing.expect(!has_method(Person, "nonexistent"));
}

fn make_interface(comptime T: type, comptime methods: anytype) type {
    return struct {
        pub usingnamespace methods;

        pub fn get_type() type {
            return T;
        }
    };
}

fn SizedContainer(comptime T: type) type {
    return struct {
        pub fn len(self: T) usize {
            return self.items.len;
        }

        pub fn is_empty(self: T) bool {
            return self.len() == 0;
        }
    };
}

const MyList = struct {
    items: []const i32,

    pub usingnamespace SizedContainer(@This());
};

test "sized container" {
    const list = MyList{ .items = &[_]i32{ 1, 2, 3, 4, 5 } };
    try std.testing.expectEqual(list.len(), 5);
    try std.testing.expect(!list.is_empty());
}

fn make_enum_with_values(comptime name: []const u8, comptime values: anytype) type {
    _ = name;
    const Type = @Type(.{
        .Int = .{
            .signedness = .unsigned,
            .bits = 8,
        },
    });
    _ = Type;

    var enum_fields: [values.len]std.builtin.Type.EnumField = undefined;
    for (values, 0..) |value, i| {
        enum_fields[i] = .{
            .name = value,
            .value = i,
        };
    }

    return @Type(.{
        .Enum = .{
            .tag_type = u8,
            .fields = &enum_fields,
            .decls = &.{},
            .is_exhaustive = true,
        },
    });
}

test "comptime enum creation" {
    const Color = make_enum_with_values("Color", &[_][]const u8{ "Red", "Green", "Blue" });
    try std.testing.expectEqual(@intFromEnum(Color.Red), 0);
    try std.testing.expectEqual(@intFromEnum(Color.Green), 1);
    try std.testing.expectEqual(@intFromEnum(Color.Blue), 2);
}

fn make_bitflags(comptime T: type) type {
    return struct {
        pub const FlagType = T;
        value: T,

        pub fn init() @This() {
            return .{ .value = 0 };
        }

        pub fn set(self: *@This(), flag: T) void {
            self.value |= flag;
        }

        pub fn clear(self: *@This(), flag: T) void {
            self.value &= ~flag;
        }

        pub fn is_set(self: @This(), flag: T) bool {
            return (self.value & flag) != 0;
        }

        pub fn toggle(self: *@This(), flag: T) void {
            self.value ^= flag;
        }
    };
}

const FileFlags = make_bitflags(u8);
const FileFlag = enum(u8) {
    read = 1,
    write = 2,
    create = 4,
    truncate = 8,
};

test "bitflags" {
    var flags = FileFlags.init();
    flags.set(@intFromEnum(FileFlag.read));
    flags.set(@intFromEnum(FileFlag.write));

    try std.testing.expect(flags.is_set(@intFromEnum(FileFlag.read)));
    try std.testing.expect(flags.is_set(@intFromEnum(FileFlag.write)));
    try std.testing.expect(!flags.is_set(@intFromEnum(FileFlag.create)));
}

fn Vector(comptime T: type, comptime size: usize) type {
    return struct {
        data: [size]T,

        const Self = @This();

        pub fn add(a: Self, b: Self) Self {
            var result: Self = undefined;
            for (0..size) |i| {
                result.data[i] = a.data[i] + b.data[i];
            }
            return result;
        }

        pub fn scale(self: Self, scalar: T) Self {
            var result: Self = undefined;
            for (0..size) |i| {
                result.data[i] = self.data[i] * scalar;
            }
            return result;
        }

        pub fn dot(a: Self, b: Self) T {
            var result: T = 0;
            for (0..size) |i| {
                result += a.data[i] * b.data[i];
            }
            return result;
        }
    };
}

test "vector operations" {
    const Vec3 = Vector(f32, 3);

    const v1 = Vec3{ .data = .{ 1.0, 2.0, 3.0 } };
    const v2 = Vec3{ .data = .{ 4.0, 5.0, 6.0 } };

    const sum = Vec3.add(v1, v2);
    try std.testing.expectApproxEqAbs(sum.data[0], 5.0, 0.001);

    const scaled = v1.scale(2.0);
    try std.testing.expectApproxEqAbs(scaled.data[0], 2.0, 0.001);

    const dot = Vec3.dot(v1, v2);
    try std.testing.expectApproxEqAbs(dot, 32.0, 0.001);
}

fn make_string_switch(comptime strings: []const []const u8) fn ([]const u8) ?usize {
    return struct {
        fn match(input: []const u8) ?usize {
            inline for (strings, 0..) |str, i| {
                if (std.mem.eql(u8, input, str)) {
                    return i;
                }
            }
            return null;
        }
    }.match;
}

test "string switch" {
    const matcher = make_string_switch(&[_][]const u8{ "one", "two", "three" });
    try std.testing.expectEqual(matcher("one"), 0);
    try std.testing.expectEqual(matcher("two"), 1);
    try std.testing.expectEqual(matcher("four"), null);
}

fn generate_lookup_table(comptime size: usize) [size]u8 {
    var table: [size]u8 = undefined;
    for (0..size) |i| {
        table[i] = @intCast(i * i);
    }
    return table;
}

const squares_table = generate_lookup_table(16);

test "lookup table" {
    try std.testing.expectEqual(squares_table[5], 25);
    try std.testing.expectEqual(squares_table[10], 100);
}

pub fn main() !void {
    std.debug.print("Comptime examples compiled successfully\n", .{});
}