use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct TurnId(pub u64);
impl TurnId {
pub const ZERO: Self = Self(0);
}
impl fmt::Display for TurnId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "turn#{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct ToolCallId(pub u64);
impl fmt::Display for ToolCallId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "tool#{}", self.0)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct IdAllocator {
next: u64,
}
impl Default for IdAllocator {
fn default() -> Self {
Self::new()
}
}
impl IdAllocator {
pub const fn new() -> Self {
Self { next: 1 }
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> u64 {
let id = self.next;
self.next = self.next.saturating_add(1);
id
}
pub fn reset(&mut self) {
self.next = 1;
}
pub fn peek(&self) -> u64 {
self.next
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn id_allocator_hands_out_monotonic_ids() {
let mut alloc = IdAllocator::new();
assert_eq!(alloc.next(), 1);
assert_eq!(alloc.next(), 2);
assert_eq!(alloc.next(), 3);
}
#[test]
fn id_allocator_reset_starts_from_one() {
let mut alloc = IdAllocator::new();
alloc.next();
alloc.next();
alloc.reset();
assert_eq!(alloc.next(), 1);
}
#[test]
fn id_types_are_distinct_at_the_type_level() {
fn only_turn(_: TurnId) {}
only_turn(TurnId(1));
}
#[test]
fn turn_id_display_format() {
assert_eq!(format!("{}", TurnId(42)), "turn#42");
assert_eq!(format!("{}", ToolCallId(7)), "tool#7");
}
#[test]
fn ids_serialize_as_bare_numbers() {
let json = serde_json::to_string(&TurnId(7)).unwrap();
assert_eq!(json, "7");
}
}