budgetkernel 0.1.2

A small, auditable, deterministic budget accounting kernel with zero heap allocation on the hot path.
Documentation
use budgetkernel::{Budget, ChargeError, Dim, Verdict};
use std::process::ExitCode;

#[derive(Copy, Clone)]
struct Task {
    name: &'static str,
    millis: u64,
    bytes: u64,
    work_units: u64,
}

fn charge_task(budget: &mut Budget, task: Task) -> Result<Verdict, ChargeError> {
    let mut verdict = Verdict::Continue;

    verdict = verdict.worst(budget.charge(Dim::Calls, 1)?);
    verdict = verdict.worst(budget.charge(Dim::Millis, task.millis)?);
    verdict = verdict.worst(budget.charge(Dim::Bytes, task.bytes)?);
    verdict = verdict.worst(budget.charge(Dim::Custom0, task.work_units)?);

    Ok(verdict)
}

fn main() -> ExitCode {
    let mut budget = match Budget::builder()
        .limit_with_warn(Dim::Calls, 4, 3)
        .limit_with_warn(Dim::Millis, 12_000, 9_000)
        .limit_with_warn(Dim::Bytes, 2_000_000, 1_600_000)
        .limit_with_warn(Dim::Custom0, 100, 80)
        .build()
    {
        Ok(budget) => budget,
        Err(error) => {
            eprintln!("failed to build task budget: {error}");
            return ExitCode::FAILURE;
        }
    };

    let tasks = [
        Task {
            name: "parse",
            millis: 1_500,
            bytes: 200_000,
            work_units: 10,
        },
        Task {
            name: "index",
            millis: 3_000,
            bytes: 650_000,
            work_units: 25,
        },
        Task {
            name: "rank",
            millis: 4_000,
            bytes: 500_000,
            work_units: 35,
        },
        Task {
            name: "summarize",
            millis: 3_200,
            bytes: 450_000,
            work_units: 20,
        },
        Task {
            name: "extra-cleanup",
            millis: 1_200,
            bytes: 120_000,
            work_units: 8,
        },
    ];

    for task in tasks {
        match charge_task(&mut budget, task) {
            Ok(Verdict::Continue) => {
                println!("{}: continue", task.name);
            }
            Ok(Verdict::Warn(dim)) => {
                println!(
                    "{}: warn on {}, reduce work or degrade quality",
                    task.name,
                    dim.name()
                );
            }
            Ok(Verdict::Exhausted(dim)) => {
                println!(
                    "{}: exhausted on {}, stop task runner",
                    task.name,
                    dim.name()
                );
                return ExitCode::SUCCESS;
            }
            Err(error) => {
                eprintln!("{}: charge failed: {error}", task.name);
                return ExitCode::FAILURE;
            }
        }
    }

    println!(
        "done: calls={:?}, millis={:?}, bytes={:?}, work_units={:?}",
        budget.spent(Dim::Calls),
        budget.spent(Dim::Millis),
        budget.spent(Dim::Bytes),
        budget.spent(Dim::Custom0)
    );

    ExitCode::SUCCESS
}