use crate::{ElicitError, ElicitErrorKind, ElicitResult};
use serde_json::Value;
#[tracing::instrument(skip(result), level = "debug")]
pub fn extract_value(result: rmcp::model::CallToolResult) -> ElicitResult<Value> {
let text = result
.content
.into_iter()
.find_map(|c| match &*c {
rmcp::model::RawContent::Text(text_content) => Some(text_content.text.clone()),
_ => None,
})
.ok_or_else(|| {
ElicitError::new(ElicitErrorKind::InvalidFormat {
expected: "text content".to_string(),
received: "empty or non-text response".to_string(),
})
})?;
serde_json::from_str(&text).or(Ok(Value::String(text)))
}
#[tracing::instrument(skip(raw), level = "debug", fields(type_name = std::any::type_name::<T>()))]
pub fn parse_integer<T>(raw: Value) -> ElicitResult<T>
where
T: TryFrom<i64> + std::fmt::Display + Copy,
{
match raw {
Value::Number(n) => {
let v = n.as_i64().ok_or_else(|| {
ElicitError::new(ElicitErrorKind::InvalidFormat {
expected: "integer".to_string(),
received: n.to_string(),
})
})?;
T::try_from(v).map_err(|_| {
ElicitError::new(ElicitErrorKind::OutOfRange {
min: "type minimum".to_string(),
max: "type maximum".to_string(),
})
})
}
Value::String(s) => {
let v: i64 = s.trim().parse().map_err(|_| {
ElicitError::new(ElicitErrorKind::InvalidFormat {
expected: "integer".to_string(),
received: s.clone(),
})
})?;
T::try_from(v).map_err(|_| {
ElicitError::new(ElicitErrorKind::OutOfRange {
min: "type minimum".to_string(),
max: "type maximum".to_string(),
})
})
}
_ => Err(ElicitError::new(ElicitErrorKind::InvalidFormat {
expected: "number or string".to_string(),
received: format!("{:?}", raw),
})),
}
}
#[tracing::instrument(skip(raw), level = "debug")]
pub fn parse_bool(raw: Value) -> ElicitResult<bool> {
match raw {
Value::Bool(b) => Ok(b),
Value::String(s) => {
let normalized = s.trim().to_lowercase();
match normalized.as_str() {
"yes" | "y" | "true" | "t" | "1" => Ok(true),
"no" | "n" | "false" | "f" | "0" => Ok(false),
_ => Err(ElicitError::new(ElicitErrorKind::InvalidFormat {
expected: "yes/no".to_string(),
received: s,
})),
}
}
_ => Err(ElicitError::new(ElicitErrorKind::InvalidFormat {
expected: "boolean or yes/no string".to_string(),
received: format!("{:?}", raw),
})),
}
}
#[tracing::instrument(skip(raw), level = "debug")]
pub fn parse_string(raw: Value) -> ElicitResult<String> {
match raw {
Value::String(s) => Ok(s),
_ => Err(ElicitError::new(ElicitErrorKind::InvalidFormat {
expected: "string".to_string(),
received: format!("{:?}", raw),
})),
}
}