use std::any::{Any, TypeId, type_name};
use std::collections::HashMap;
use std::hash::{BuildHasherDefault, Hasher};
use crate::wasm_compat::{WasmCompatSend, WasmCompatSync};
type AnyMap = HashMap<TypeId, Box<dyn AnyClone>, BuildHasherDefault<IdHasher>>;
#[derive(Default)]
struct IdHasher(u64);
impl Hasher for IdHasher {
#[inline]
fn write_u64(&mut self, id: u64) {
self.0 = id;
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
for &byte in bytes {
self.0 = self.0.rotate_left(8) ^ u64::from(byte);
}
}
#[inline]
fn finish(&self) -> u64 {
self.0
}
}
trait AnyClone: Any + WasmCompatSend + WasmCompatSync {
fn clone_box(&self) -> Box<dyn AnyClone>;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any>;
fn type_name(&self) -> &'static str;
}
impl<T: Clone + WasmCompatSend + WasmCompatSync + 'static> AnyClone for T {
fn clone_box(&self) -> Box<dyn AnyClone> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
fn type_name(&self) -> &'static str {
type_name::<T>()
}
}
impl Clone for Box<dyn AnyClone> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}
#[derive(Default, Clone)]
pub(crate) struct TypeMap {
map: Option<Box<AnyMap>>,
}
impl TypeMap {
pub(crate) const EMPTY: TypeMap = TypeMap { map: None };
pub(crate) fn insert<T: Clone + WasmCompatSend + WasmCompatSync + 'static>(
&mut self,
val: T,
) -> Option<T> {
self.map
.get_or_insert_with(Default::default)
.insert(TypeId::of::<T>(), Box::new(val))
.and_then(|prev| prev.into_any().downcast::<T>().ok())
.map(|boxed| *boxed)
}
pub(crate) fn get<T: WasmCompatSend + WasmCompatSync + 'static>(&self) -> Option<&T> {
self.map
.as_ref()
.and_then(|map| map.get(&TypeId::of::<T>()))
.and_then(|boxed| (**boxed).as_any().downcast_ref::<T>())
}
pub(crate) fn require<T: WasmCompatSend + WasmCompatSync + 'static>(
&self,
) -> Result<&T, MissingExtension> {
self.get::<T>().ok_or(MissingExtension(type_name::<T>()))
}
pub(crate) fn get_mut<T: WasmCompatSend + WasmCompatSync + 'static>(
&mut self,
) -> Option<&mut T> {
self.map
.as_mut()
.and_then(|map| map.get_mut(&TypeId::of::<T>()))
.and_then(|boxed| (**boxed).as_any_mut().downcast_mut::<T>())
}
pub(crate) fn remove<T: WasmCompatSend + WasmCompatSync + 'static>(&mut self) -> Option<T> {
self.map
.as_mut()
.and_then(|map| map.remove(&TypeId::of::<T>()))
.and_then(|boxed| boxed.into_any().downcast::<T>().ok())
.map(|boxed| *boxed)
}
pub(crate) fn contains<T: WasmCompatSend + WasmCompatSync + 'static>(&self) -> bool {
self.map
.as_ref()
.is_some_and(|map| map.contains_key(&TypeId::of::<T>()))
}
pub(crate) fn len(&self) -> usize {
self.map.as_ref().map_or(0, |map| map.len())
}
fn type_names(&self) -> Vec<&'static str> {
self.map
.as_ref()
.map(|map| map.values().map(|v| (**v).type_name()).collect())
.unwrap_or_default()
}
}
fn debug_type_map(f: &mut std::fmt::Formatter<'_>, name: &str, map: &TypeMap) -> std::fmt::Result {
let mut dbg = f.debug_struct(name);
dbg.field("entries", &map.len());
if map.len() > 0 {
dbg.field("types", &map.type_names());
}
dbg.finish()
}
#[derive(Default, Clone)]
pub struct ToolCallExtensions {
inner: TypeMap,
}
impl ToolCallExtensions {
pub(crate) const EMPTY: ToolCallExtensions = ToolCallExtensions {
inner: TypeMap::EMPTY,
};
pub const fn new() -> Self {
Self::EMPTY
}
pub fn insert<T: Clone + WasmCompatSend + WasmCompatSync + 'static>(
&mut self,
val: T,
) -> Option<T> {
self.inner.insert(val)
}
pub fn get<T: WasmCompatSend + WasmCompatSync + 'static>(&self) -> Option<&T> {
self.inner.get::<T>()
}
pub fn require<T: WasmCompatSend + WasmCompatSync + 'static>(
&self,
) -> Result<&T, MissingExtension> {
self.inner.require::<T>()
}
pub fn get_mut<T: WasmCompatSend + WasmCompatSync + 'static>(&mut self) -> Option<&mut T> {
self.inner.get_mut::<T>()
}
pub fn remove<T: WasmCompatSend + WasmCompatSync + 'static>(&mut self) -> Option<T> {
self.inner.remove::<T>()
}
pub fn contains<T: WasmCompatSend + WasmCompatSync + 'static>(&self) -> bool {
self.inner.contains::<T>()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl std::fmt::Debug for ToolCallExtensions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
debug_type_map(f, "ToolCallExtensions", &self.inner)
}
}
#[derive(Default, Clone)]
pub struct ToolResultExtensions {
inner: TypeMap,
}
impl ToolResultExtensions {
pub const fn new() -> Self {
Self {
inner: TypeMap::EMPTY,
}
}
pub fn insert<T: Clone + WasmCompatSend + WasmCompatSync + 'static>(
&mut self,
val: T,
) -> Option<T> {
self.inner.insert(val)
}
pub fn get<T: WasmCompatSend + WasmCompatSync + 'static>(&self) -> Option<&T> {
self.inner.get::<T>()
}
pub fn require<T: WasmCompatSend + WasmCompatSync + 'static>(
&self,
) -> Result<&T, MissingExtension> {
self.inner.require::<T>()
}
pub fn get_mut<T: WasmCompatSend + WasmCompatSync + 'static>(&mut self) -> Option<&mut T> {
self.inner.get_mut::<T>()
}
pub fn remove<T: WasmCompatSend + WasmCompatSync + 'static>(&mut self) -> Option<T> {
self.inner.remove::<T>()
}
pub fn contains<T: WasmCompatSend + WasmCompatSync + 'static>(&self) -> bool {
self.inner.contains::<T>()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl std::fmt::Debug for ToolResultExtensions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
debug_type_map(f, "ToolResultExtensions", &self.inner)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("required tool extension of type `{0}` was not found")]
pub struct MissingExtension(pub &'static str);
#[cfg(not(target_family = "wasm"))]
const _: fn() = || {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<ToolCallExtensions>();
assert_send_sync::<ToolResultExtensions>();
};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert_and_get_returns_value() {
let mut extensions = ToolCallExtensions::new();
assert_eq!(extensions.insert(42u32), None);
assert_eq!(extensions.get::<u32>(), Some(&42));
}
#[test]
fn get_missing_type_returns_none() {
let extensions = ToolCallExtensions::new();
assert_eq!(extensions.get::<u32>(), None);
}
#[test]
fn insert_overwrites_and_returns_previous() {
let mut extensions = ToolCallExtensions::new();
assert_eq!(extensions.insert(1u32), None);
assert_eq!(extensions.insert(2u32), Some(1));
assert_eq!(extensions.get::<u32>(), Some(&2));
}
#[test]
fn different_types_are_independent() {
let mut extensions = ToolCallExtensions::new();
extensions.insert(42u32);
extensions.insert("hello".to_string());
assert_eq!(extensions.get::<u32>(), Some(&42));
assert_eq!(extensions.get::<String>(), Some(&"hello".to_string()));
}
#[test]
fn contains_returns_true_when_present() {
let mut extensions = ToolCallExtensions::new();
extensions.insert(42u32);
assert!(extensions.contains::<u32>());
assert!(!extensions.contains::<String>());
}
#[test]
fn clone_produces_independent_copy() {
let mut extensions = ToolCallExtensions::new();
extensions.insert(42u32);
let mut cloned = extensions.clone();
cloned.insert(99u32);
assert_eq!(extensions.get::<u32>(), Some(&42));
assert_eq!(cloned.get::<u32>(), Some(&99));
}
#[test]
fn clone_deep_copies_inner_value() {
let mut extensions = ToolCallExtensions::new();
extensions.insert(vec![1u8, 2, 3]);
let mut cloned = extensions.clone();
cloned.get_mut::<Vec<u8>>().unwrap().push(4);
assert_eq!(extensions.get::<Vec<u8>>(), Some(&vec![1, 2, 3]));
assert_eq!(cloned.get::<Vec<u8>>(), Some(&vec![1, 2, 3, 4]));
}
#[test]
fn debug_shows_entry_count_and_types() {
let mut extensions = ToolCallExtensions::new();
extensions.insert(42u32);
extensions.insert("hi".to_string());
let debug = format!("{:?}", extensions);
assert!(debug.contains("entries: 2"));
assert!(debug.contains("u32"));
assert!(debug.contains("String"));
}
#[test]
fn empty_extensions_is_default() {
let extensions = ToolCallExtensions::default();
assert!(!extensions.contains::<u32>());
}
#[test]
fn empty_extensions_has_no_allocation() {
let extensions = ToolCallExtensions::new();
assert!(extensions.inner.map.is_none());
}
#[test]
fn get_mut_modifies_in_place() {
let mut extensions = ToolCallExtensions::new();
extensions.insert(42u32);
if let Some(val) = extensions.get_mut::<u32>() {
*val = 99;
}
assert_eq!(extensions.get::<u32>(), Some(&99));
}
#[test]
fn remove_returns_value_and_clears_entry() {
let mut extensions = ToolCallExtensions::new();
extensions.insert(42u32);
assert_eq!(extensions.remove::<u32>(), Some(42));
assert!(!extensions.contains::<u32>());
}
#[test]
fn remove_missing_type_returns_none() {
let mut extensions = ToolCallExtensions::new();
assert_eq!(extensions.remove::<u32>(), None);
}
#[test]
fn require_present_returns_value() {
let mut extensions = ToolCallExtensions::new();
extensions.insert(42u32);
assert_eq!(extensions.require::<u32>().copied(), Ok(42));
}
#[test]
fn require_missing_names_the_type() {
let extensions = ToolCallExtensions::new();
let err = extensions.require::<u32>().unwrap_err();
assert!(err.to_string().contains("u32"));
}
#[test]
fn len_and_is_empty_track_entries() {
let mut extensions = ToolCallExtensions::new();
assert!(extensions.is_empty());
assert_eq!(extensions.len(), 0);
extensions.insert(1u32);
extensions.insert("two".to_string());
assert!(!extensions.is_empty());
assert_eq!(extensions.len(), 2);
extensions.remove::<u32>();
assert_eq!(extensions.len(), 1);
}
#[test]
fn many_distinct_types_round_trip_through_id_hasher() {
#[derive(Clone, PartialEq, Debug)]
struct A(u8);
#[derive(Clone, PartialEq, Debug)]
struct B(u16);
#[derive(Clone, PartialEq, Debug)]
struct C(u32);
#[derive(Clone, PartialEq, Debug)]
struct D(u64);
let mut extensions = ToolCallExtensions::new();
extensions.insert(A(1));
extensions.insert(B(2));
extensions.insert(C(3));
extensions.insert(D(4));
extensions.insert(5u8);
extensions.insert(6i64);
extensions.insert("seven".to_string());
extensions.insert(8.0f64);
assert_eq!(extensions.len(), 8);
assert_eq!(extensions.get::<A>(), Some(&A(1)));
assert_eq!(extensions.get::<B>(), Some(&B(2)));
assert_eq!(extensions.get::<C>(), Some(&C(3)));
assert_eq!(extensions.get::<D>(), Some(&D(4)));
assert_eq!(extensions.get::<u8>(), Some(&5));
assert_eq!(extensions.get::<i64>(), Some(&6));
assert_eq!(extensions.get::<String>(), Some(&"seven".to_string()));
assert_eq!(extensions.get::<f64>(), Some(&8.0));
}
#[test]
fn result_extensions_round_trip_and_require() {
#[derive(Clone, Debug, PartialEq)]
struct RawHeaders(Vec<(String, String)>);
let mut extensions = ToolResultExtensions::new();
assert!(extensions.is_empty());
extensions.insert(RawHeaders(vec![("x-req-id".into(), "abc".into())]));
assert_eq!(
extensions.get::<RawHeaders>(),
Some(&RawHeaders(vec![("x-req-id".into(), "abc".into())]))
);
assert!(extensions.require::<u32>().is_err());
assert_eq!(extensions.len(), 1);
let debug = format!("{extensions:?}");
assert!(debug.contains("ToolResultExtensions"));
assert!(debug.contains("entries: 1"));
}
}