use serde_json::{Value, json};
use crate::error::ToolError;
use crate::protocol::ServerState;
#[cfg(feature = "ask")]
mod ask;
mod describe_table;
mod execute;
mod list_tables;
mod query;
mod schema_dump;
mod vector_search;
pub fn list(read_only: bool) -> Vec<Value> {
let mut tools = Vec::with_capacity(7);
tools.push(list_tables::metadata());
tools.push(describe_table::metadata());
tools.push(query::metadata());
if !read_only {
tools.push(execute::metadata());
}
tools.push(schema_dump::metadata());
tools.push(vector_search::metadata());
#[cfg(feature = "ask")]
tools.push(ask::metadata());
tools
}
pub fn dispatch(name: &str, args: Value, state: &mut ServerState) -> Result<String, ToolError> {
match name {
"list_tables" => list_tables::handle(args, state),
"describe_table" => describe_table::handle(args, state),
"query" => query::handle(args, state),
"execute" => execute::handle(args, state),
"schema_dump" => schema_dump::handle(args, state),
"vector_search" => vector_search::handle(args, state),
#[cfg(feature = "ask")]
"ask" => ask::handle(args, state),
other => Err(ToolError::new(format!(
"unknown tool: `{other}`. Call `tools/list` to see available tools."
))),
}
}
pub(crate) const TOOL_OUTPUT_CAP_BYTES: usize = 64 * 1024;
pub(crate) fn is_safe_identifier(s: &str) -> bool {
let mut chars = s.chars();
match chars.next() {
Some(c) if c.is_ascii_alphabetic() || c == '_' => {}
_ => return false,
}
chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
}
pub(crate) fn value_to_json(v: &sqlrite::Value) -> Value {
match v {
sqlrite::Value::Integer(i) => json!(i),
sqlrite::Value::Real(f) => {
if f.is_finite() { json!(f) } else { Value::Null }
}
sqlrite::Value::Text(s) => json!(s),
sqlrite::Value::Bool(b) => json!(b),
sqlrite::Value::Vector(v) => {
let arr: Vec<Value> = v
.iter()
.map(|f| if f.is_finite() { json!(f) } else { Value::Null })
.collect();
Value::Array(arr)
}
sqlrite::Value::Null => Value::Null,
}
}