1use crate::DCPError;
4
5#[repr(C)]
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct ToolInvocation {
9 pub tool_id: u32,
11 pub arg_layout: u64,
13 pub args_offset: u32,
15 pub args_len: u32,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[repr(u8)]
22pub enum ArgType {
23 Null = 0,
24 Bool = 1,
25 I32 = 2,
26 I64 = 3,
27 F64 = 4,
28 String = 5,
29 Bytes = 6,
30 Array = 7,
31 Object = 8,
32}
33
34impl ArgType {
35 pub fn from_u8(value: u8) -> Option<Self> {
37 match value {
38 0 => Some(Self::Null),
39 1 => Some(Self::Bool),
40 2 => Some(Self::I32),
41 3 => Some(Self::I64),
42 4 => Some(Self::F64),
43 5 => Some(Self::String),
44 6 => Some(Self::Bytes),
45 7 => Some(Self::Array),
46 8 => Some(Self::Object),
47 _ => None,
48 }
49 }
50}
51
52impl ToolInvocation {
53 pub const SIZE: usize = 24; pub const MAX_ARGS: usize = 16;
59
60 pub fn new(tool_id: u32, arg_layout: u64, args_offset: u32, args_len: u32) -> Self {
62 Self {
63 tool_id,
64 arg_layout,
65 args_offset,
66 args_len,
67 }
68 }
69
70 #[inline(always)]
72 pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, DCPError> {
73 let bytes = bytes.as_ref();
74 if bytes.len() < Self::SIZE {
75 return Err(DCPError::InsufficientData);
76 }
77 if bytes.len() != Self::SIZE {
78 return Err(DCPError::ValidationFailed);
79 }
80 if bytes[4..8].iter().any(|&byte| byte != 0) {
81 return Err(DCPError::ValidationFailed);
82 }
83
84 Ok(Self {
85 tool_id: u32::from_le_bytes(bytes[0..4].try_into().unwrap()),
86 arg_layout: u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
87 args_offset: u32::from_le_bytes(bytes[16..20].try_into().unwrap()),
88 args_len: u32::from_le_bytes(bytes[20..24].try_into().unwrap()),
89 })
90 }
91
92 #[inline(always)]
94 pub fn as_bytes(&self) -> [u8; Self::SIZE] {
95 let mut bytes = [0u8; Self::SIZE];
96 bytes[0..4].copy_from_slice(&self.tool_id.to_le_bytes());
97 bytes[8..16].copy_from_slice(&self.arg_layout.to_le_bytes());
98 bytes[16..20].copy_from_slice(&self.args_offset.to_le_bytes());
99 bytes[20..24].copy_from_slice(&self.args_len.to_le_bytes());
100 bytes
101 }
102
103 pub fn get_arg_type(&self, index: usize) -> Option<ArgType> {
105 if index >= Self::MAX_ARGS {
106 return None;
107 }
108 let shift = index * 4;
109 let type_bits = ((self.arg_layout >> shift) & 0xF) as u8;
110 ArgType::from_u8(type_bits)
111 }
112
113 pub fn set_arg_type(&mut self, index: usize, arg_type: ArgType) {
115 if index >= Self::MAX_ARGS {
116 return;
117 }
118 let shift = index * 4;
119 self.arg_layout &= !(0xF << shift);
121 self.arg_layout |= (arg_type as u64) << shift;
123 }
124
125 pub fn arg_count(&self) -> usize {
127 let mut count = 0;
128 for i in 0..Self::MAX_ARGS {
129 if let Some(arg_type) = self.get_arg_type(i) {
130 if arg_type != ArgType::Null {
131 count += 1;
132 } else {
133 break; }
135 }
136 }
137 count
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn test_invocation_size() {
147 assert_eq!(std::mem::size_of::<ToolInvocation>(), ToolInvocation::SIZE);
148 }
149
150 #[test]
151 fn test_invocation_round_trip() {
152 let inv = ToolInvocation::new(42, 0x12345678, 100, 200);
153 let bytes = inv.as_bytes();
154 let parsed = ToolInvocation::from_bytes(bytes).unwrap();
155
156 assert_eq!(parsed.tool_id, 42);
157 assert_eq!(parsed.arg_layout, 0x12345678);
158 assert_eq!(parsed.args_offset, 100);
159 assert_eq!(parsed.args_len, 200);
160 }
161
162 #[test]
163 fn test_arg_type_encoding() {
164 let mut inv = ToolInvocation::new(1, 0, 0, 0);
165
166 inv.set_arg_type(0, ArgType::String);
167 inv.set_arg_type(1, ArgType::I32);
168 inv.set_arg_type(2, ArgType::Bool);
169
170 assert_eq!(inv.get_arg_type(0), Some(ArgType::String));
171 assert_eq!(inv.get_arg_type(1), Some(ArgType::I32));
172 assert_eq!(inv.get_arg_type(2), Some(ArgType::Bool));
173 assert_eq!(inv.get_arg_type(3), Some(ArgType::Null));
174 }
175
176 #[test]
177 fn test_arg_count() {
178 let mut inv = ToolInvocation::new(1, 0, 0, 0);
179 assert_eq!(inv.arg_count(), 0);
180
181 inv.set_arg_type(0, ArgType::String);
182 inv.set_arg_type(1, ArgType::I32);
183 assert_eq!(inv.arg_count(), 2);
184 }
185}