typetui 0.2.0

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

const Pkg = struct {
    name: []const u8,
    path: []const u8,
    dependencies: []const *Pkg = &.{},
};

const build_pkgs = &.{
    Pkg{
        .name = "zap",
        .path = "deps/zap/src/main.zig",
    },
    Pkg{
        .name = "clap",
        .path = "deps/clap/src/main.zig",
    },
};

const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("stdlib.h");
});

pub extern "C" fn c_malloc(size: usize) ?*anyopaque;
pub extern "C" fn c_free(ptr: ?*anyopaque) void;

export fn zig_add(a: i32, b: i32) i32 {
    return a + b;
}

export fn zig_process(data: [*]const u8, len: usize) i32 {
    var sum: i32 = 0;
    for (0..len) |i| {
        sum += data[i];
    }
    return sum;
}

const Frame = @Frame(asyncTask);

fn asyncTask(id: u32, delay_ms: u32) u32 {
    std.time.sleep(delay_ms * std.time.ns_per_ms);
    std.debug.print("Task {} completed\n", .{id});
    return id * 10;
}

fn asyncExample() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    var frame1 = try allocator.create(Frame);
    defer allocator.destroy(frame1);
    var frame2 = try allocator.create(Frame);
    defer allocator.destroy(frame2);
    frame1.* = async asyncTask(1, 100);
    frame2.* = async asyncTask(2, 50);
    const result1 = await frame1;
    const result2 = await frame2;

    std.debug.print("Results: {}, {}\n", .{ result1, result2 });
}

fn StateMachine(comptime State: type, comptime Event: type, comptime transitions: anytype) type {
    return struct {
        state: State,

        pub fn init(initial: State) @This() {
            return .{ .state = initial };
        }

        pub fn handle(self: *@This(), event: Event) ?State {
            inline for (transitions) |t| {
                if (self.state == t.from and event == t.event) {
                    self.state = t.to;
                    return t.to;
                }
            }
            return null;
        }
    };
}

const TrafficLightState = enum { red, yellow, green };
const LightEvent = enum { timer, emergency };

const TrafficLight = StateMachine(TrafficLightState, LightEvent, &.{
    .{ .from = .red, .event = .timer, .to = .green },
    .{ .from = .green, .event = .timer, .to = .yellow },
    .{ .from = .yellow, .event = .timer, .to = .red },
    .{ .from = .green, .event = .emergency, .to = .red },
    .{ .from = .yellow, .event = .emergency, .to = .red },
});

fn RingBuffer(comptime T: type, comptime capacity: usize) type {
    return struct {
        buffer: [capacity]T = undefined,
        head: usize = 0,
        tail: usize = 0,
        count: usize = 0,

        const Self = @This();

        pub fn push(self: *Self, item: T) !void {
            if (self.count >= capacity) return error.Full;
            self.buffer[self.tail] = item;
            self.tail = (self.tail + 1) % capacity;
            self.count += 1;
        }

        pub fn pop(self: *Self) ?T {
            if (self.count == 0) return null;
            const item = self.buffer[self.head];
            self.head = (self.head + 1) % capacity;
            self.count -= 1;
            return item;
        }

        pub fn isEmpty(self: *const Self) bool {
            return self.count == 0;
        }

        pub fn isFull(self: *const Self) bool {
            return self.count == capacity;
        }
    };
}

const BitSet = struct {
    bits: u64,

    pub fn init() BitSet {
        return .{ .bits = 0 };
    }

    pub fn set(self: *BitSet, bit: u6) void {
        self.bits |= @as(u64, 1) << bit;
    }

    pub fn clear(self: *BitSet, bit: u6) void {
        self.bits &= ~(@as(u64, 1) << bit);
    }

    pub fn isSet(self: *const BitSet, bit: u6) bool {
        return (self.bits >> bit) & 1 == 1;
    }

    pub fn toggle(self: *BitSet, bit: u6) void {
        self.bits ^= @as(u64, 1) << bit;
    }
};

fn vectorAdd(a: [4]f32, b: [4]f32) [4]f32 {
    const va: @Vector(4, f32) = a;
    const vb: @Vector(4, f32) = b;
    const result = va + vb;
    return @bitCast([4]f32, result);
}

const Serializer = struct {
    buffer: []u8,
    pos: usize,

    pub fn init(buf: []u8) Serializer {
        return .{ .buffer = buf, .pos = 0 };
    }

    pub fn writeInt(self: *Serializer, value: u32) !void {
        if (self.pos + 4 > self.buffer.len) return error.NoSpace;
        std.mem.writeIntBig(u32, self.buffer[self.pos..][0..4], value);
        self.pos += 4;
    }

    pub fn writeBytes(self: *Serializer, bytes: []const u8) !void {
        if (self.pos + bytes.len > self.buffer.len) return error.NoSpace;
        @memcpy(self.buffer[self.pos..][0..bytes.len], bytes);
        self.pos += bytes.len;
    }

    pub fn getWritten(self: *const Serializer) []const u8 {
        return self.buffer[0..self.pos];
    }
};

const Deserializer = struct {
    buffer: []const u8,
    pos: usize,

    pub fn init(buf: []const u8) Deserializer {
        return .{ .buffer = buf, .pos = 0 };
    }

    pub fn readInt(self: *Deserializer) !u32 {
        if (self.pos + 4 > self.buffer.len) return error.Underflow;
        const value = std.mem.readIntBig(u32, self.buffer[self.pos..][0..4]);
        self.pos += 4;
        return value;
    }

    pub fn readBytes(self: *Deserializer, len: usize) ![]const u8 {
        if (self.pos + len > self.buffer.len) return error.Underflow;
        const result = self.buffer[self.pos..][0..len];
        self.pos += len;
        return result;
    }
};

fn validateStruct(comptime T: type) void {
    const info = @typeInfo(T);
    if (info != .Struct) {
        @compileError("Expected struct type");
    }

    inline for (info.Struct.fields) |field| {
        if (field.type == []const u8) {
            @compileLog("Field", field.name, "is a string");
        }
    }
}

const TestStruct = struct {
    name: []const u8,
    count: u32,
};

comptime {
    validateStruct(TestStruct);
}

fn Lazy(comptime T: type, comptime initFn: fn () T) type {
    return struct {
        var value: ?T = null;
        var mutex = std.Thread.Mutex{};

        pub fn get() *T {
            if (value == null) {
                mutex.lock();
                defer mutex.unlock();
                if (value == null) {
                    value = initFn();
                }
            }
            return &value.?;
        }
    };
}

const Command = union(enum) {
    add: struct { x: i32, y: i32 },
    sub: struct { x: i32, y: i32 },
    mul: struct { x: i32, y: i32 },

    pub fn execute(self: Command) i32 {
        return switch (self) {
            .add => |args| args.x + args.y,
            .sub => |args| args.x - args.y,
            .mul => |args| args.x * args.y,
        };
    }
};

test "ring buffer" {
    var buf = RingBuffer(u32, 4){};
    try buf.push(1);
    try buf.push(2);
    try buf.push(3);

    std.testing.expectEqual(@as(u32, 1), buf.pop().?);
    try buf.push(4);
    try buf.push(5);

    std.testing.expect(buf.isFull());
    std.testing.expectEqual(@as(u32, 2), buf.pop().?);
}

test "bit set" {
    var bs = BitSet.init();
    bs.set(5);
    std.testing.expect(bs.isSet(5));
    bs.toggle(5);
    std.testing.expect(!bs.isSet(5));
}

test "command pattern" {
    const cmd = Command{ .add = .{ .x = 10, .y = 20 } };
    std.testing.expectEqual(@as(i32, 30), cmd.execute());
}

pub fn main() !void {
    var light = TrafficLight.init(.red);
    _ = light.handle(.timer);
    std.debug.print("Light state: {}\n", .{light.state});
    var ring = RingBuffer(u32, 8){};
    try ring.push(100);
    try ring.push(200);
    while (ring.pop()) |val| {
        std.debug.print("Ring value: {}\n", .{val});
    }
    var buf: [100]u8 = undefined;
    var ser = Serializer.init(&buf);
    try ser.writeInt(0x12345678);
    try ser.writeBytes("hello");

    var deser = Deserializer.init(ser.getWritten());
    const int_val = try deser.readInt();
    const bytes = try deser.readBytes(5);
    std.debug.print("Deserialized: {x}, {s}\n", .{ int_val, bytes });
    const commands = &[_]Command{
        .{ .add = .{ .x = 1, .y = 2 } },
        .{ .sub = .{ .x = 10, .y = 3 } },
        .{ .mul = .{ .x = 4, .y = 5 } },
    };

    for (commands) |cmd| {
        std.debug.print("Result: {}\n", .{cmd.execute()});
    }
    const ptr = c_malloc(100);
    if (ptr) |p| {
        std.debug.print("Allocated {} bytes via C\n", .{@as(usize, 100)});
        c_free(p);
    }
    asyncExample() catch |err| {
        std.debug.print("Async error: {}\n", .{err});
    };
}