ajhahn.de
← Flash
Flash 168 lines
// cookbook — the COOKBOOK.md recipes as one compiled module.
//
// Every fenced Flash example in COOKBOOK.md lives here as real code, so a
// recipe can never drift into syntax the compiler rejects, and the test
// blocks run the recipes against the standard library for real.
//
// Lives in examples/register/ (post-v0.5 grammar — the frozen stage0
// bootstrap compiler cannot parse it; gated by `zig build fixpoint`).

use std
use core

const Allocator = std.mem.Allocator

// --- One arena per job ---

// Allocate freely inside the job, release everything with one deinit.
// Whatever must outlive the arena is copied out to the caller's allocator
// before the function returns.
pub fn greeting(child Allocator, name []u8, attempt u32) ![]u8 {
    var arena = core.arena.ArenaAllocator.init(child)
    defer arena.deinit()
    alloc := arena.allocator()

    line := try core.fmt.allocPrint(alloc, "hello {s}, attempt {d}", .{ name, attempt })
    return child.dupe(u8, line)
}

// --- Error handling ---

pub const ConfigError = error{ NotFound, OutOfMemory }

// Originate with `error.Name`, propagate with `try`, recover with `catch`.
pub fn valueOf(table [][]u8, key []u8) ConfigError![]u8 {
    for line in table {
        if core.mem.indexOfScalar(u8, line, '=') |eq| {
            if core.mem.eql(u8, line[0..eq], key) {
                return line[eq + 1 ..]
            }
        }
    }
    return error.NotFound
}

pub fn portOrDefault(s []u8) u16 {
    return core.fmt.parseInt(u16, s, 10) catch 8080
}

// errdefer releases what is already built when a later step fails.
pub fn dupePair(alloc Allocator, a []u8, b []u8) ![2][]u8 {
    first := try alloc.dupe(u8, a)
    errdefer alloc.free(first)
    second := try alloc.dupe(u8, b)
    return .{ first, second }
}

// --- Cast chains ---

pub const Header = struct {
    magic u32,
    len u32,
}

// Spell #ptrCast outermost: the lowered Zig is then already in the order
// zig fmt canonicalizes to.
pub fn asHeader(raw [*]mut u8) *mut Header {
    return #ptrCast(#alignCast(raw))
}

// --- Bit manipulation ---

pub const READY u32 = 1 << 5

pub fn isReady(reg u32) bool {
    return reg & READY != 0
}

pub fn green(rgba u32) u8 {
    return #truncate((rgba >> 8) & 0xFF)
}

pub fn nextSeq(seq u8) u8 {
    return seq +% 1
}

// --- Struct literals and the formatter ---

pub const Config = struct {
    name []u8 = "",
    retries u8 = 3,
    verbose bool = false,
}

// The formatter keeps initializers on one line — write them collapsed.
pub fn defaultConfig() Config {
    return .{}
}

pub fn verboseConfig(name []u8) Config {
    return .{ .name = name, .retries = 5, .verbose = true }
}

pub var scratch [64]u8 = [_]u8{0} ** 64

// --- JSON ---

pub fn portOf(alloc Allocator, src []u8) !i64 {
    doc := try core.json.parse(alloc, src)
    value := doc.get("port") orelse return error.MissingPort
    return switch value {
        .int => |n| n,
        else => error.MissingPort,
    }
}

test "greeting survives its arena" {
    s := try greeting(std.testing.allocator, "port", 2)
    defer std.testing.allocator.free(s)
    try std.testing.expect(core.mem.eql(u8, s, "hello port, attempt 2"))
}

test "valueOf finds keys and reports missing ones" {
    table := [_][]u8{ "host=10.0.0.1", "port=8080" }
    v := try valueOf(&table, "port")
    try std.testing.expect(core.mem.eql(u8, v, "8080"))
    try std.testing.expectError(error.NotFound, valueOf(&table, "user"))
    try std.testing.expectEqual(8080, portOrDefault("8080"))
    try std.testing.expectEqual(8080, portOrDefault("not a number"))
}

test "dupePair hands back two independent copies" {
    pair := try dupePair(std.testing.allocator, "a", "bb")
    defer {
        std.testing.allocator.free(pair[0])
        std.testing.allocator.free(pair[1])
    }
    try std.testing.expect(core.mem.eql(u8, pair[1], "bb"))
}

test "asHeader reinterprets raw bytes in place" {
    var buf [8]u8 align(4) = [_]u8{0} ** 8
    h := asHeader(&buf)
    h.magic = 0x464c5348
    try std.testing.expectEqual(0x48, buf[0])
}

test "bit recipes" {
    try std.testing.expect(isReady(READY))
    try std.testing.expect(!isReady(0))
    try std.testing.expectEqual(0x34, green(0x00123456))
    try std.testing.expectEqual(0, nextSeq(255))
}

test "struct literal recipes" {
    cfg := verboseConfig("uart0")
    try std.testing.expect(cfg.verbose)
    try std.testing.expectEqual(5, cfg.retries)
    try std.testing.expectEqual(3, defaultConfig().retries)
    try std.testing.expectEqual(0, scratch[10])
}

test "portOf reads a JSON document" {
    var arena = core.arena.ArenaAllocator.init(std.testing.allocator)
    defer arena.deinit()
    n := try portOf(arena.allocator(), "{\"host\":\"example\",\"port\":8080}")
    try std.testing.expectEqual(8080, n)
}