use crate::logoi::input::tool::{FunctionCall, FunctionParameter, FunctionType};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct FunctionParamRaw<'a> {
pub name: &'a str,
pub ty: &'a str,
pub description: &'a str,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct FunctionCallRaw<'a> {
pub name: &'a str,
pub description: &'a str,
pub parameters: &'a [FunctionParamRaw<'a>],
}
impl<'a> FunctionCallRaw<'a> {
pub fn to_fn_call(self) -> Result<FunctionCall, String> {
let name = self.name.to_string();
let description = if self.description.is_empty() {
None
} else {
Some(self.description.to_string())
};
let mut parameters = Vec::with_capacity(self.parameters.len());
for raw in self.parameters {
parameters.push(FunctionParameter {
name: raw.name.to_string(),
_type: parse_raw_type(raw.ty)?,
description: if raw.description.is_empty() {
None
} else {
Some(raw.description.to_string())
},
required: !is_option_type_str(raw.ty),
});
}
Ok(FunctionCall {
name,
description,
parameters,
})
}
}
fn is_option_type_str(ty: &str) -> bool {
let t = ty.trim();
t.starts_with("Option<") || t.starts_with("Option <") || t == "Option"
}
fn parse_raw_type(_type: &str) -> Result<FunctionType, String> {
let t = _type.trim();
if let Some(inner) = strip_wrapper(t, "Option") {
return Ok(FunctionType::Option(Box::new(parse_raw_type(inner)?)));
}
if let Some(inner) = strip_wrapper(t, "Vec") {
return Ok(FunctionType::Array(Box::new(parse_raw_type(inner)?)));
}
if let Some(inner) = strip_wrapper(t, "HashMap").or_else(|| strip_wrapper(t, "BTreeMap")) {
if let Some(idx) = top_level_comma(inner) {
let v_ty = inner[idx + 1..].trim();
return Ok(FunctionType::Map(Box::new(parse_raw_type(v_ty)?)));
}
}
match t {
"String" | "&str" | "&'static str" | "Cow<'static, str>" | "string" => {
Ok(FunctionType::String)
}
"bool" | "boolean" => Ok(FunctionType::Boolean),
"i8" | "i16" | "i32" | "i64" | "i128" | "isize" | "u8" | "u16" | "u32" | "u64" | "u128"
| "usize" | "f32" | "f64" | "number" => Ok(FunctionType::Number),
other => Err(format!(
"raw schema: type `{other}` is not yet supported by FunctionCallRaw::to_fn_call"
)),
}
}
fn strip_wrapper<'a>(t: &'a str, name: &str) -> Option<&'a str> {
let rest = t.strip_prefix(name)?.trim_start();
let rest = rest.strip_prefix('<')?;
let rest = rest.strip_suffix('>')?;
Some(rest)
}
fn top_level_comma(s: &str) -> Option<usize> {
let mut depth: i32 = 0;
for (i, c) in s.char_indices() {
match c {
'<' => depth += 1,
'>' => depth -= 1,
',' if depth == 0 => return Some(i),
_ => {}
}
}
None
}