Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Book macro

Quick Start

// SPDX-License-Identifier: AGPL-3.0
// Copyright (C) 2026 SAP2B

use kyaaa::{Str, book, page};

// 1. Schemas defined via page!
page!(
    struct Dog {
        name: Str<16>,
        age: u8,
    }
    struct Cat {
        name: Str<16>,
        age: u8,
    }
);

fn main() {
    // 2. Heterogeneous registry with 2 Dogs and 2 Cats using struct literals
    let shelter = book!(
        dog1 => Dog {
            name: Str::from_str("Rex"),
            age: 3,
        },
        dog2 => Dog {
            name: Str::from_str("Thor"),
            age: 5,
        },
        cat1 => Cat {
            name: Str::from_str("Luna"),
            age: 2,
        },
        cat2 => Cat {
            name: Str::from_str("Mina"),
            age: 1,
        }
    );

    // 3. Direct access to all 4 elements via generated tokens
    assert_eq!(shelter.dog1().get().age, 3);
    assert_eq!(shelter.dog2().get().age, 5);
    assert_eq!(shelter.cat1().get().age, 2);
    assert_eq!(shelter.cat2().get().age, 1);

    // 4. Safe mutation on specific instances (setters continue working via KyaaaConstInto)
    shelter.cat1().set().age(3); // Luna had a birthday!
    shelter.dog2().set().age(6); // Thor had a birthday!

    assert_eq!(shelter.cat1().get().age, 3);
    assert_eq!(shelter.dog2().get().age, 6);

    // 5. Type filtering now returns exactly 2 of each
    let dog_count = shelter.list::<Dog>().count();
    let cat_count = shelter.list::<Cat>().count();

    assert_eq!(dog_count, 2);
    assert_eq!(cat_count, 2);

    println!("Shelter updated: {dog_count} dogs and {cat_count} cats registered!");
}

Examples

game.rs

// SPDX-License-Identifier: AGPL-3.0
// Copyright (C) 2026 SAP2B

use colorful::{Colorful, HSL};
use kyaaa::Str;

kyaaa::page!(
    struct Game {
        name: Str<32>,
        favorite: bool,
    }
    struct User {
        name: Str<32>,
    }
);

trait Hsl: Colorful + Sized {
    fn print_hsl(self) {
        let msg = self.gradient_with_color(HSL::new(0.0, 1.0, 0.5), HSL::new(0.833, 1.0, 0.5));
        println!("{msg}");
    }
}

impl Hsl for String {}
impl Hsl for &str {}

fn main() {
    let hlist = kyaaa::book!(
        zelda => Game {
            name: Str::from_str("Zelda"),
            favorite: true,
        },
        nier => Game {
            name: Str::from_str("Nier Automata"),
            favorite: true,
        },
        lol => Game {
            name: Str::from_str("League of Legends"),
            favorite: false,
        },
        sap => User {
            name: Str::from_str("SAP2B"),
        }
    );

    let before_hlist = [hlist.zelda().get(), hlist.nier().get(), hlist.lol().get()];
    let sap = hlist.sap().get();

    format!("Before Book: {hlist:?} -> {before_hlist:#?}, {sap:#?}").print_hsl();

    hlist.zelda().set().name("New Zelda").favorite(true);
    hlist.nier().set().name("New Nier Automata").favorite(true);
    hlist.lol().set().name("cs2").favorite(true);

    let after_hlist = [hlist.zelda().get(), hlist.nier().get(), hlist.lol().get()];
    format!("After Book: {hlist:?} -> {after_hlist:#?}, {sap:#?}").print_hsl();
}

cargo run

gameasm.rs

// SPDX-License-Identifier: AGPL-3.0
// Copyright (C) 2026 SAP2B

use kyaaa::{Str, book, page};

page!(
    struct Game {
        name: Str<32>,
        favorite: bool,
    }
    struct User {
        name: Str<32>,
    }
);

fn main() {
    let hlist = book!(
        nier => Game {
            name: Str::from_str("Nier Automata"),
            favorite: true,
        },
        sap => User {
            name: Str::from_str("SAP2B"),
        },
    );

    hlist.nier().set().name("NiER Automata").favorite(true);

    black_box(hlist.nier().get());
    black_box(hlist.sap().get());
}

#[inline(always)]
fn black_box<T>(dummy: T) -> T {
    core::hint::black_box(dummy)
}

assembly proof

cargo asm --example gameasm --release gameasm::main
# Cargo.toml

[profile.release]
opt-level = 3
panic = "abort"
lto = true
codegen-units = 1
strip = true

output

tests

// SPDX-License-Identifier: AGPL-3.0
// Copyright (C) 2026 SAP2B

use kyaaa::{Str, book, page};

page!(
    struct Game {
        name: Str<32>,
        favorite: bool,
    }
    struct User {
        name: Str<32>,
    }
);

#[test]
fn page_struct_zeroed_memory_and_padding() {
    let game = Game::default();
    let bytes = game.to_bytes();

    assert_eq!(bytes.len(), std::mem::size_of::<Game>());
    assert!(bytes.iter().all(|&b| b == 0));
}

#[test]
fn book_ids_sequential_assignment() {
    let hlist = book!(
        first  => Game { name: Str::empty(), favorite: false },
        second => User { name: Str::empty() },
        third  => Game { name: Str::empty(), favorite: false }
    );

    assert_eq!(hlist.first().id(), 0);
    assert_eq!(hlist.second().id(), 1);
    assert_eq!(hlist.third().id(), 2);
}

#[test]
fn book_multiple_entries_same_type() {
    let hlist = book!(
        g1 => Game { name: Str::empty(), favorite: false },
        g2 => Game { name: Str::empty(), favorite: false }
    );

    hlist.g1().set().name("G1");
    hlist.g2().set().name("G2");

    let games: Vec<&'static Game> = hlist.list::<Game>().collect();
    assert_eq!(games.len(), 2);
    assert_eq!(hlist.g1().get().name, games[0].name);
    assert_eq!(hlist.g2().get().name, games[1].name);
}

#[test]
fn page_bytes_roundtrip_identity() {
    let mut original = Game::default();
    original.name("Elden Ring").favorite(true);

    let bytes = original.to_bytes();
    let (restored, _) = Game::from_bytes(bytes).unwrap();
    let double_bytes = restored.to_bytes();

    assert_eq!(bytes, double_bytes);
}

#[test]
fn page_struct_derived_traits() {
    let default_game = Game::default();
    assert_eq!(default_game.favorite, false);

    let mut game1 = Game::default();
    game1.name("Mario").favorite(true);

    let game2 = game1;
    let game3 = game1;

    assert_eq!(game1, game2);
    assert_eq!(game2, game3);
}

#[test]
fn page_from_bytes_with_remainder() {
    let mut original = Game::default();
    original.name("Tetris").favorite(true);

    let mut bytes = original.to_bytes().to_vec();
    bytes.extend_from_slice(&[0xFF, 0xEE, 0xDD]);

    let (restored, rest) = Game::from_bytes(&bytes).unwrap();

    assert_eq!(original.name, restored.name);
    assert_eq!(original.favorite, restored.favorite);
    assert_eq!(rest, &[0xFF, 0xEE, 0xDD]);
}

#[test]
fn book_dynamic_get_mut() {
    let hlist = book!(
        player1 => User { name: Str::empty() },
        player2 => User { name: Str::empty() }
    );

    hlist.player1().set().name("P1");
    hlist.player2().set().name("P2");

    let p1_id = hlist.player1().id();

    let mut_user = hlist.get_mut::<User>(p1_id).unwrap();
    mut_user.name("P1_Edited");

    assert_eq!(
        hlist.player1().get().name,
        hlist.get::<User>(p1_id).unwrap().name
    );
}

#[test]
fn book_invalid_id_access() {
    let hlist = book!(
        solo => User { name: Str::empty() }
    );

    let invalid_id = 99;

    assert!(hlist.get::<User>(invalid_id).is_none());
    assert!(hlist.get_mut::<User>(invalid_id).is_none());
}

#[test]
fn page_struct_methods() {
    let mut game = Game::default();
    game.name("Zelda").favorite(true);

    assert_eq!(game.favorite, true);
    assert_eq!(Game::FIELDS, &["name", "favorite"]);
}

#[test]
fn page_bytes_conversion() {
    let mut original = User::default();
    original.name("admin");

    let bytes = original.to_bytes();
    let (restored, rest) = User::from_bytes(bytes).unwrap();

    assert_eq!(original.name, restored.name);
    assert!(rest.is_empty());
}

#[test]
fn page_from_bytes_insufficient_length() {
    let empty_bytes: &[u8] = &[];
    assert!(User::from_bytes(empty_bytes).is_none());
}

#[test]
fn book_static_references_and_mutation() {
    let hlist = book!(
        zelda => Game { name: Str::empty(), favorite: false },
        admin => User { name: Str::empty() }
    );

    hlist.zelda().set().name("Zelda").favorite(true);
    hlist.admin().set().name("admin");

    assert_eq!(hlist.zelda().get().favorite, true);

    hlist.zelda().set().favorite(false);
    assert_eq!(hlist.zelda().get().favorite, false);
}

#[test]
fn book_dynamic_methods() {
    let hlist = book!(
        zelda => Game { name: Str::empty(), favorite: false },
        sap   => User { name: Str::empty() },
        nier  => Game { name: Str::empty(), favorite: false }
    );

    hlist.zelda().set().name("Zelda").favorite(true);
    hlist.sap().set().name("SAP2B");
    hlist.nier().set().name("Nier Automata").favorite(false);

    let games: Vec<&'static Game> = hlist.list::<Game>().collect();
    assert_eq!(games.len(), 2);
    assert_eq!(games[0].favorite, true);
    assert_eq!(games[1].favorite, false);

    let user_id = hlist.sap().id();
    let fetched_user = hlist.get::<User>(user_id).unwrap();
    assert_eq!(fetched_user.name, hlist.sap().get().name);

    assert!(hlist.get::<Game>(user_id).is_none());
}