use crate::platform_store::PlatformStore;
use crate::tools::ToolExecutionResult;
use crate::traits::{SessionFileSystem, ToolContext};
use serde_json::Value;
use std::str::FromStr;
pub fn get_str<'a>(args: &'a Value, key: &str) -> Option<&'a str> {
args.get(key)
.and_then(|v| v.as_str())
.filter(|s| !s.is_empty())
}
pub fn require_str<'a>(args: &'a Value, key: &str) -> Result<&'a str, ToolExecutionResult> {
get_str(args, key).ok_or_else(|| {
ToolExecutionResult::tool_error(format!("Missing required parameter: {key}"))
})
}
pub fn require_str_trimmed<'a>(args: &'a Value, key: &str) -> Result<&'a str, ToolExecutionResult> {
args.get(key)
.and_then(Value::as_str)
.map(str::trim)
.filter(|s| !s.is_empty())
.ok_or_else(|| {
ToolExecutionResult::tool_error(format!("Missing required parameter: {key}"))
})
}
pub fn require_str_nonblank<'a>(
args: &'a Value,
key: &str,
) -> Result<&'a str, ToolExecutionResult> {
args.get(key)
.and_then(Value::as_str)
.filter(|s| !s.trim().is_empty())
.ok_or_else(|| {
ToolExecutionResult::tool_error(format!("Missing required parameter: {key}"))
})
}
pub fn parse_id<T: FromStr>(raw: &str, label: &str) -> Result<T, ToolExecutionResult> {
raw.parse::<T>()
.map_err(|_| ToolExecutionResult::tool_error(format!("Invalid {label}: {raw}")))
}
pub fn require_id<T: FromStr>(args: &Value, key: &str) -> Result<T, ToolExecutionResult> {
let raw = require_str(args, key)?;
parse_id(raw, key)
}
pub fn get_platform_store(
context: &ToolContext,
) -> Result<&dyn PlatformStore, ToolExecutionResult> {
context
.platform_store
.as_ref()
.map(|store| store.as_ref())
.ok_or_else(|| {
ToolExecutionResult::tool_error(
"Platform management not available: platform_store context is missing. Ensure the platform_management capability is enabled.",
)
})
}
pub fn require_file_store(
context: &ToolContext,
) -> Result<&dyn SessionFileSystem, ToolExecutionResult> {
context
.file_store
.as_ref()
.map(|store| store.as_ref())
.ok_or_else(|| ToolExecutionResult::tool_error("File system not available"))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::typed_id::SessionId;
use serde_json::json;
#[test]
fn get_str_filters_empty_but_not_whitespace() {
let args = json!({ "a": "", "b": " ", "c": "x" });
assert_eq!(get_str(&args, "a"), None);
assert_eq!(get_str(&args, "b"), Some(" "));
assert_eq!(get_str(&args, "c"), Some("x"));
assert_eq!(get_str(&args, "missing"), None);
}
#[test]
fn require_str_rejects_truly_empty_keeps_raw() {
let args = json!({ "a": "", "b": " x ", "c": "x" });
assert!(require_str(&args, "a").is_err());
assert!(require_str(&args, "missing").is_err());
assert_eq!(require_str(&args, "b").unwrap(), " x ");
assert_eq!(require_str(&args, "c").unwrap(), "x");
}
#[test]
fn require_str_trimmed_trims_and_rejects_blank() {
let args = json!({ "blank": " ", "padded": " x ", "ok": "y" });
assert!(require_str_trimmed(&args, "blank").is_err());
assert!(require_str_trimmed(&args, "missing").is_err());
assert_eq!(require_str_trimmed(&args, "padded").unwrap(), "x");
assert_eq!(require_str_trimmed(&args, "ok").unwrap(), "y");
}
#[test]
fn require_str_nonblank_rejects_blank_keeps_raw() {
let args = json!({ "blank": " ", "padded": " x ", "ok": "y" });
assert!(require_str_nonblank(&args, "blank").is_err());
assert!(require_str_nonblank(&args, "missing").is_err());
assert_eq!(require_str_nonblank(&args, "padded").unwrap(), " x ");
assert_eq!(require_str_nonblank(&args, "ok").unwrap(), "y");
}
#[test]
fn require_id_parse_failure_maps_to_invalid_key() {
let args = json!({ "session_id": "not-a-valid-id" });
let err = require_id::<SessionId>(&args, "session_id").unwrap_err();
match err {
ToolExecutionResult::ToolError(msg) => {
assert_eq!(msg, "Invalid session_id: not-a-valid-id");
}
other => panic!("expected ToolError, got {other:?}"),
}
}
#[test]
fn require_id_missing_maps_to_missing_parameter() {
let args = json!({});
let err = require_id::<SessionId>(&args, "session_id").unwrap_err();
match err {
ToolExecutionResult::ToolError(msg) => {
assert_eq!(msg, "Missing required parameter: session_id");
}
other => panic!("expected ToolError, got {other:?}"),
}
}
#[test]
fn parse_id_uses_custom_label() {
let err = parse_id::<SessionId>("garbage", "harness id").unwrap_err();
match err {
ToolExecutionResult::ToolError(msg) => {
assert_eq!(msg, "Invalid harness id: garbage");
}
other => panic!("expected ToolError, got {other:?}"),
}
}
}