use serde_json::{Value, json};
use super::Rail;
use crate::mcp::tools::ToolResult;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Outcome {
Reply(Value),
Sent,
Refused(String),
}
pub const PASS_FIELD: &str = "pass";
pub fn with_pass<R: Rail>(rail: &R, input: &Value, audience: &str) -> Result<Value, String> {
let Value::Object(object) = input else {
return Err("pass_for needs an object input".to_owned());
};
if object.contains_key(PASS_FIELD) {
return Err(format!(
"the input already carries {PASS_FIELD}; with pass_for the ticket puts it in"
));
}
let pass = rail.pass_for(audience).map_err(|error| error.to_string())?;
let mut object = object.clone();
object.insert(PASS_FIELD.to_owned(), Value::String(pass));
Ok(Value::Object(object))
}
pub fn request<R: Rail>(
rail: &R,
subject: &str,
input: &Value,
reply: bool,
pass_for: Option<&str>,
) -> Outcome {
let minted;
let input = match pass_for {
None => input,
Some(audience) => match with_pass(rail, input, audience) {
Ok(carrying) => {
minted = carrying;
&minted
}
Err(words) => return Outcome::Refused(words),
},
};
let bytes = serde_json::to_vec(input).map_or(usize::MAX, |bytes| bytes.len());
if bytes > rail.request_bytes() {
return Outcome::Refused(format!(
"the request is over {} bytes and was not sent",
rail.request_bytes()
));
}
match rail.request(subject, input, reply) {
Ok(Some(answer)) => Outcome::Reply(answer),
Ok(None) => Outcome::Sent,
Err(error) => Outcome::Refused(error.to_string()),
}
}
pub fn call<R: Rail>(
rail: &R,
subject: &str,
input: &Value,
reply: bool,
pass_for: Option<&str>,
) -> ToolResult {
match request(rail, subject, input, reply, pass_for) {
Outcome::Reply(answer) => ToolResult::ok(answer),
Outcome::Sent => ToolResult::ok(json!({ "subject": subject, "sent": true })),
Outcome::Refused(words) => ToolResult::failed(&words, json!({ "subject": subject })),
}
}
#[cfg(test)]
#[path = "call_tests.rs"]
mod tests;