#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MintKind {
Reasoning,
EncryptedReasoning,
Block,
Output,
Tool,
Text,
}
impl MintKind {
pub fn for_wire_index(self, index: u64) -> StreamPartId {
StreamPartId::minted(self, index)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StreamPartId(Repr);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Repr {
Wire(String),
Minted {
kind: MintKind,
index: u64,
},
}
impl From<String> for StreamPartId {
fn from(id: String) -> Self {
Self(Repr::Wire(id))
}
}
impl From<&str> for StreamPartId {
fn from(id: &str) -> Self {
Self(Repr::Wire(id.to_owned()))
}
}
impl StreamPartId {
pub fn wire(id: impl Into<String>) -> Self {
Self(Repr::Wire(id.into()))
}
pub const fn minted(kind: MintKind, index: u64) -> Self {
Self(Repr::Minted { kind, index })
}
pub fn is_minted(&self) -> bool {
match &self.0 {
Repr::Wire(_) => false,
Repr::Minted { .. } => true,
}
}
pub(crate) fn wire_str(&self) -> Option<&str> {
match &self.0 {
Repr::Wire(wire) => Some(wire),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct WireId(String);
impl WireId {
pub fn new(id: impl Into<String>) -> Option<Self> {
let id = id.into();
if id.is_empty() { None } else { Some(Self(id)) }
}
pub fn into_string(self) -> String {
self.0
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug)]
pub struct SyntheticIds {
kind: MintKind,
next: u64,
}
impl SyntheticIds {
pub fn new(kind: MintKind) -> Self {
Self { kind, next: 0 }
}
pub fn output() -> Self {
Self::new(MintKind::Output)
}
pub fn tool() -> Self {
Self::new(MintKind::Tool)
}
pub fn text() -> Self {
Self::new(MintKind::Text)
}
pub fn mint(&mut self) -> StreamPartId {
let id = self.for_index(self.next);
self.next = self.next.saturating_add(1);
id
}
fn for_index(&self, index: u64) -> StreamPartId {
self.kind.for_wire_index(index)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_absent_provider_handle_is_none_not_empty() {
assert!(WireId::new("").is_none());
assert_eq!(
WireId::new("rs_123").expect("non-empty").into_string(),
"rs_123"
);
}
#[test]
fn mint_counts_up_per_stream() {
let mut ids = SyntheticIds::new(MintKind::Reasoning);
assert_eq!(ids.mint(), StreamPartId::minted(MintKind::Reasoning, 0));
assert_eq!(ids.mint(), StreamPartId::minted(MintKind::Reasoning, 1));
}
#[test]
fn minted_keys_are_distinct_across_kinds_and_indices() {
let kinds = [
MintKind::Reasoning,
MintKind::Block,
MintKind::Output,
MintKind::Tool,
MintKind::Text,
];
let mut seen = std::collections::HashSet::new();
for kind in kinds {
for index in [0u64, 1, 7, u64::MAX] {
assert!(
seen.insert(StreamPartId::minted(kind, index)),
"collision at {kind:?}:{index}"
);
}
}
}
}