use redis::{Client, Commands, Connection};
use serde_json::{Value, json};
use std::collections::HashMap;
use tracing::{debug, info};
use crate::DriverCategory;
use crate::types::{Driver, DriverParameter};
use crate::{DriverCallback, DriverContext, DriverError, DriverResult};
fn get_redis_connection(host: &str, port: u16, password: &str, db: usize) -> DriverResult<Connection> {
let url = if password.is_empty() { format!("redis://{}:{}/", host, port) } else { format!("redis://:{}@{}:{}/{}", password, host, port, db) };
debug!("Connecting to Redis at {}:{}", host, port);
let client = Client::open(url).map_err(|e| DriverError::execution(format!("Failed to create Redis client: {}", e)))?;
let conn = client.get_connection().map_err(|e| DriverError::execution(format!("Failed to connect to Redis: {}", e)))?;
info!("Successfully connected to Redis at {}:{}", host, port);
return Ok(conn);
}
fn get_param_string(params: &HashMap<String, Value>, name: &str) -> DriverResult<String> {
return params.get(name).and_then(|v| v.as_str()).map(|s| s.to_string()).ok_or_else(|| DriverError::missing_parameter(name));
}
fn get_param_u64(params: &HashMap<String, Value>, name: &str, default: u64) -> u64 {
return params.get(name).and_then(|v| v.as_u64()).unwrap_or(default);
}
fn get_param_usize(params: &HashMap<String, Value>, name: &str, default: usize) -> usize {
return params.get(name).and_then(|v| v.as_u64()).map(|v| v as usize).unwrap_or(default);
}
#[derive(Debug)]
pub struct RedisSetDriver;
#[async_trait::async_trait]
impl Driver for RedisSetDriver {
fn name(&self) -> &str {
return "redis_set";
}
fn description(&self) -> &str {
return "Set a key-value pair in Redis";
}
fn usage_hint(&self) -> &str {
return "Use this skill when the user needs to store data in Redis";
}
fn category(&self) -> DriverCategory {
return DriverCategory::Database;
}
fn parameters(&self) -> Vec<DriverParameter> {
return vec![
DriverParameter {
name: "host".to_string(),
param_type: "string".to_string(),
description: "Redis host".to_string(),
required: true,
default: None,
example: Some(Value::String("localhost".to_string())),
enum_values: None,
},
DriverParameter {
name: "port".to_string(),
param_type: "integer".to_string(),
description: "Redis port".to_string(),
required: false,
default: Some(Value::Number(6379.into())),
example: Some(Value::Number(6379.into())),
enum_values: None,
},
DriverParameter {
name: "password".to_string(),
param_type: "string".to_string(),
description: "Redis password".to_string(),
required: false,
default: Some(Value::String("".to_string())),
example: Some(Value::String("".to_string())),
enum_values: None,
},
DriverParameter {
name: "db".to_string(),
param_type: "integer".to_string(),
description: "Redis database number".to_string(),
required: false,
default: Some(Value::Number(0.into())),
example: Some(Value::Number(0.into())),
enum_values: None,
},
DriverParameter {
name: "key".to_string(),
param_type: "string".to_string(),
description: "Redis key".to_string(),
required: true,
default: None,
example: Some(Value::String("user:100".to_string())),
enum_values: None,
},
DriverParameter {
name: "value".to_string(),
param_type: "string".to_string(),
description: "Value to store".to_string(),
required: true,
default: None,
example: Some(Value::String("John Doe".to_string())),
enum_values: None,
},
DriverParameter {
name: "ttl".to_string(),
param_type: "integer".to_string(),
description: "Time to live in seconds".to_string(),
required: false,
default: None,
example: Some(Value::Number(3600.into())),
enum_values: None,
},
];
}
fn example_call(&self) -> DriverResult<Value> {
return Ok(json!({
"action": "redis_set",
"parameters": {
"host": "localhost",
"key": "user:100",
"value": "John Doe",
"ttl": 3600
}
}));
}
fn example_output(&self) -> String {
return "Successfully set key 'user:100'".to_string();
}
async fn execute(
&self,
parameters: &HashMap<String, Value>,
_callback: Option<&dyn DriverCallback>,
_context: Option<&DriverContext>,
) -> DriverResult<String> {
debug!("Executing redis_set driver");
let host = get_param_string(parameters, "host")?;
let port = get_param_u64(parameters, "port", 6379) as u16;
let password = parameters.get("password").and_then(|v| v.as_str()).unwrap_or("");
let db = get_param_usize(parameters, "db", 0);
let key = get_param_string(parameters, "key")?;
let value = get_param_string(parameters, "value")?;
let ttl = parameters.get("ttl").and_then(|v| v.as_u64());
let mut conn = get_redis_connection(&host, port, password, db)?;
debug!("Setting key: {}", key);
let _: () = conn.set(&key, &value).map_err(|e| DriverError::execution(format!("Failed to set key: {}", e)))?;
if let Some(ttl_secs) = ttl {
debug!("Setting TTL for key {}: {} seconds", key, ttl_secs);
let _: () = conn.expire(&key, ttl_secs as i64).map_err(|e| DriverError::execution(format!("Failed to set TTL: {}", e)))?;
}
info!("Successfully set key '{}'", key);
return Ok(format!("Successfully set key '{}'", key));
}
}
#[derive(Debug)]
pub struct RedisGetDriver;
#[async_trait::async_trait]
impl Driver for RedisGetDriver {
fn name(&self) -> &str {
return "redis_get";
}
fn description(&self) -> &str {
return "Get a value from Redis by key";
}
fn usage_hint(&self) -> &str {
return "Use this skill when the user needs to retrieve data from Redis";
}
fn category(&self) -> DriverCategory {
return DriverCategory::Database;
}
fn parameters(&self) -> Vec<DriverParameter> {
return vec![
DriverParameter {
name: "host".to_string(),
param_type: "string".to_string(),
description: "Redis host".to_string(),
required: true,
default: None,
example: Some(Value::String("localhost".to_string())),
enum_values: None,
},
DriverParameter {
name: "port".to_string(),
param_type: "integer".to_string(),
description: "Redis port".to_string(),
required: false,
default: Some(Value::Number(6379.into())),
example: Some(Value::Number(6379.into())),
enum_values: None,
},
DriverParameter {
name: "password".to_string(),
param_type: "string".to_string(),
description: "Redis password".to_string(),
required: false,
default: Some(Value::String("".to_string())),
example: Some(Value::String("".to_string())),
enum_values: None,
},
DriverParameter {
name: "db".to_string(),
param_type: "integer".to_string(),
description: "Redis database number".to_string(),
required: false,
default: Some(Value::Number(0.into())),
example: Some(Value::Number(0.into())),
enum_values: None,
},
DriverParameter {
name: "key".to_string(),
param_type: "string".to_string(),
description: "Redis key".to_string(),
required: true,
default: None,
example: Some(Value::String("user:100".to_string())),
enum_values: None,
},
];
}
fn example_call(&self) -> DriverResult<Value> {
return Ok(json!({
"action": "redis_get",
"parameters": {
"host": "localhost",
"key": "user:100"
}
}));
}
fn example_output(&self) -> String {
return "John Doe".to_string();
}
async fn execute(
&self,
parameters: &HashMap<String, Value>,
_callback: Option<&dyn DriverCallback>,
_context: Option<&DriverContext>,
) -> DriverResult<String> {
debug!("Executing redis_get driver");
let host = get_param_string(parameters, "host")?;
let port = get_param_u64(parameters, "port", 6379) as u16;
let password = parameters.get("password").and_then(|v| v.as_str()).unwrap_or("");
let db = get_param_usize(parameters, "db", 0);
let key = get_param_string(parameters, "key")?;
let mut conn = get_redis_connection(&host, port, password, db)?;
debug!("Getting key: {}", key);
let value: Option<String> = conn.get(&key).map_err(|e| DriverError::execution(format!("Failed to get key: {}", e)))?;
match value {
Some(v) => {
info!("Successfully retrieved key '{}'", key);
return Ok(v);
}
None => {
info!("Key '{}' not found", key);
return Ok("null".to_string());
}
}
}
}
#[derive(Debug)]
pub struct RedisDelDriver;
#[async_trait::async_trait]
impl Driver for RedisDelDriver {
fn name(&self) -> &str {
return "redis_del";
}
fn description(&self) -> &str {
return "Delete a key from Redis";
}
fn usage_hint(&self) -> &str {
return "Use this skill when the user needs to delete data from Redis";
}
fn category(&self) -> DriverCategory {
return DriverCategory::Database;
}
fn parameters(&self) -> Vec<DriverParameter> {
return vec![
DriverParameter {
name: "host".to_string(),
param_type: "string".to_string(),
description: "Redis host".to_string(),
required: true,
default: None,
example: Some(Value::String("localhost".to_string())),
enum_values: None,
},
DriverParameter {
name: "port".to_string(),
param_type: "integer".to_string(),
description: "Redis port".to_string(),
required: false,
default: Some(Value::Number(6379.into())),
example: Some(Value::Number(6379.into())),
enum_values: None,
},
DriverParameter {
name: "password".to_string(),
param_type: "string".to_string(),
description: "Redis password".to_string(),
required: false,
default: Some(Value::String("".to_string())),
example: Some(Value::String("".to_string())),
enum_values: None,
},
DriverParameter {
name: "db".to_string(),
param_type: "integer".to_string(),
description: "Redis database number".to_string(),
required: false,
default: Some(Value::Number(0.into())),
example: Some(Value::Number(0.into())),
enum_values: None,
},
DriverParameter {
name: "key".to_string(),
param_type: "string".to_string(),
description: "Redis key to delete".to_string(),
required: true,
default: None,
example: Some(Value::String("user:100".to_string())),
enum_values: None,
},
];
}
fn example_call(&self) -> DriverResult<Value> {
return Ok(json!({
"action": "redis_del",
"parameters": {
"host": "localhost",
"key": "user:100"
}
}));
}
fn example_output(&self) -> String {
return "Successfully deleted key 'user:100'".to_string();
}
async fn execute(
&self,
parameters: &HashMap<String, Value>,
_callback: Option<&dyn DriverCallback>,
_context: Option<&DriverContext>,
) -> DriverResult<String> {
debug!("Executing redis_del driver");
let host = get_param_string(parameters, "host")?;
let port = get_param_u64(parameters, "port", 6379) as u16;
let password = parameters.get("password").and_then(|v| v.as_str()).unwrap_or("");
let db = get_param_usize(parameters, "db", 0);
let key = get_param_string(parameters, "key")?;
let mut conn = get_redis_connection(&host, port, password, db)?;
debug!("Deleting key: {}", key);
let deleted: i32 = conn.del(&key).map_err(|e| DriverError::execution(format!("Failed to delete key: {}", e)))?;
if deleted > 0 {
info!("Successfully deleted key '{}'", key);
return Ok(format!("Successfully deleted key '{}'", key));
} else {
info!("Key '{}' not found", key);
return Ok(format!("Key '{}' not found", key));
}
}
}
#[derive(Debug)]
pub struct RedisKeysDriver;
#[async_trait::async_trait]
impl Driver for RedisKeysDriver {
fn name(&self) -> &str {
return "redis_keys";
}
fn description(&self) -> &str {
return "Find keys matching a pattern in Redis";
}
fn usage_hint(&self) -> &str {
return "Use this skill when the user needs to list keys in Redis";
}
fn category(&self) -> DriverCategory {
return DriverCategory::Database;
}
fn parameters(&self) -> Vec<DriverParameter> {
return vec![
DriverParameter {
name: "host".to_string(),
param_type: "string".to_string(),
description: "Redis host".to_string(),
required: true,
default: None,
example: Some(Value::String("localhost".to_string())),
enum_values: None,
},
DriverParameter {
name: "port".to_string(),
param_type: "integer".to_string(),
description: "Redis port".to_string(),
required: false,
default: Some(Value::Number(6379.into())),
example: Some(Value::Number(6379.into())),
enum_values: None,
},
DriverParameter {
name: "password".to_string(),
param_type: "string".to_string(),
description: "Redis password".to_string(),
required: false,
default: Some(Value::String("".to_string())),
example: Some(Value::String("".to_string())),
enum_values: None,
},
DriverParameter {
name: "db".to_string(),
param_type: "integer".to_string(),
description: "Redis database number".to_string(),
required: false,
default: Some(Value::Number(0.into())),
example: Some(Value::Number(0.into())),
enum_values: None,
},
DriverParameter {
name: "pattern".to_string(),
param_type: "string".to_string(),
description: "Key pattern (e.g., 'user:*')".to_string(),
required: false,
default: Some(Value::String("*".to_string())),
example: Some(Value::String("user:*".to_string())),
enum_values: None,
},
];
}
fn example_call(&self) -> DriverResult<Value> {
return Ok(json!({
"action": "redis_keys",
"parameters": {
"host": "localhost",
"pattern": "user:*"
}
}));
}
fn example_output(&self) -> String {
return r#"["user:100", "user:101"]"#.to_string();
}
async fn execute(
&self,
parameters: &HashMap<String, Value>,
_callback: Option<&dyn DriverCallback>,
_context: Option<&DriverContext>,
) -> DriverResult<String> {
debug!("Executing redis_keys driver");
let host = get_param_string(parameters, "host")?;
let port = get_param_u64(parameters, "port", 6379) as u16;
let password = parameters.get("password").and_then(|v| v.as_str()).unwrap_or("");
let db = get_param_usize(parameters, "db", 0);
let pattern = parameters.get("pattern").and_then(|v| v.as_str()).unwrap_or("*");
let mut conn = get_redis_connection(&host, port, password, db)?;
debug!("Searching keys with pattern: {}", pattern);
let keys: Vec<String> = conn.keys(pattern).map_err(|e| DriverError::execution(format!("Failed to list keys: {}", e)))?;
info!("Found {} keys matching pattern", keys.len());
return Ok(json!(keys).to_string());
}
}
#[derive(Debug)]
pub struct RedisHSetDriver;
#[async_trait::async_trait]
impl Driver for RedisHSetDriver {
fn name(&self) -> &str {
return "redis_hset";
}
fn description(&self) -> &str {
return "Set a field in a Redis hash";
}
fn usage_hint(&self) -> &str {
return "Use this skill when the user needs to store structured data in Redis";
}
fn category(&self) -> DriverCategory {
return DriverCategory::Database;
}
fn parameters(&self) -> Vec<DriverParameter> {
return vec![
DriverParameter {
name: "host".to_string(),
param_type: "string".to_string(),
description: "Redis host".to_string(),
required: true,
default: None,
example: Some(Value::String("localhost".to_string())),
enum_values: None,
},
DriverParameter {
name: "port".to_string(),
param_type: "integer".to_string(),
description: "Redis port".to_string(),
required: false,
default: Some(Value::Number(6379.into())),
example: Some(Value::Number(6379.into())),
enum_values: None,
},
DriverParameter {
name: "password".to_string(),
param_type: "string".to_string(),
description: "Redis password".to_string(),
required: false,
default: Some(Value::String("".to_string())),
example: Some(Value::String("".to_string())),
enum_values: None,
},
DriverParameter {
name: "db".to_string(),
param_type: "integer".to_string(),
description: "Redis database number".to_string(),
required: false,
default: Some(Value::Number(0.into())),
example: Some(Value::Number(0.into())),
enum_values: None,
},
DriverParameter {
name: "key".to_string(),
param_type: "string".to_string(),
description: "Hash key".to_string(),
required: true,
default: None,
example: Some(Value::String("user:100".to_string())),
enum_values: None,
},
DriverParameter {
name: "field".to_string(),
param_type: "string".to_string(),
description: "Field name".to_string(),
required: true,
default: None,
example: Some(Value::String("name".to_string())),
enum_values: None,
},
DriverParameter {
name: "value".to_string(),
param_type: "string".to_string(),
description: "Value to set".to_string(),
required: true,
default: None,
example: Some(Value::String("John Doe".to_string())),
enum_values: None,
},
];
}
fn example_call(&self) -> DriverResult<Value> {
return Ok(json!({
"action": "redis_hset",
"parameters": {
"host": "localhost",
"key": "user:100",
"field": "name",
"value": "John Doe"
}
}));
}
fn example_output(&self) -> String {
return "Successfully set field 'name' in hash 'user:100'".to_string();
}
async fn execute(
&self,
parameters: &HashMap<String, Value>,
_callback: Option<&dyn DriverCallback>,
_context: Option<&DriverContext>,
) -> DriverResult<String> {
debug!("Executing redis_hset driver");
let host = get_param_string(parameters, "host")?;
let port = get_param_u64(parameters, "port", 6379) as u16;
let password = parameters.get("password").and_then(|v| v.as_str()).unwrap_or("");
let db = get_param_usize(parameters, "db", 0);
let key = get_param_string(parameters, "key")?;
let field = get_param_string(parameters, "field")?;
let value = get_param_string(parameters, "value")?;
let mut conn = get_redis_connection(&host, port, password, db)?;
debug!("Setting hash field: {}:{} = {}", key, field, value);
let _: i32 = conn.hset(&key, &field, &value).map_err(|e| DriverError::execution(format!("Failed to set hash field: {}", e)))?;
info!("Successfully set field '{}' in hash '{}'", field, key);
return Ok(format!("Successfully set field '{}' in hash '{}'", field, key));
}
}
#[derive(Debug)]
pub struct RedisHGetDriver;
#[async_trait::async_trait]
impl Driver for RedisHGetDriver {
fn name(&self) -> &str {
return "redis_hget";
}
fn description(&self) -> &str {
return "Get a field from a Redis hash";
}
fn usage_hint(&self) -> &str {
return "Use this skill when the user needs to retrieve structured data from Redis";
}
fn category(&self) -> DriverCategory {
return DriverCategory::Database;
}
fn parameters(&self) -> Vec<DriverParameter> {
return vec![
DriverParameter {
name: "host".to_string(),
param_type: "string".to_string(),
description: "Redis host".to_string(),
required: true,
default: None,
example: Some(Value::String("localhost".to_string())),
enum_values: None,
},
DriverParameter {
name: "port".to_string(),
param_type: "integer".to_string(),
description: "Redis port".to_string(),
required: false,
default: Some(Value::Number(6379.into())),
example: Some(Value::Number(6379.into())),
enum_values: None,
},
DriverParameter {
name: "password".to_string(),
param_type: "string".to_string(),
description: "Redis password".to_string(),
required: false,
default: Some(Value::String("".to_string())),
example: Some(Value::String("".to_string())),
enum_values: None,
},
DriverParameter {
name: "db".to_string(),
param_type: "integer".to_string(),
description: "Redis database number".to_string(),
required: false,
default: Some(Value::Number(0.into())),
example: Some(Value::Number(0.into())),
enum_values: None,
},
DriverParameter {
name: "key".to_string(),
param_type: "string".to_string(),
description: "Hash key".to_string(),
required: true,
default: None,
example: Some(Value::String("user:100".to_string())),
enum_values: None,
},
DriverParameter {
name: "field".to_string(),
param_type: "string".to_string(),
description: "Field name".to_string(),
required: true,
default: None,
example: Some(Value::String("name".to_string())),
enum_values: None,
},
];
}
fn example_call(&self) -> DriverResult<Value> {
return Ok(json!({
"action": "redis_hget",
"parameters": {
"host": "localhost",
"key": "user:100",
"field": "name"
}
}));
}
fn example_output(&self) -> String {
return "John Doe".to_string();
}
async fn execute(
&self,
parameters: &HashMap<String, Value>,
_callback: Option<&dyn DriverCallback>,
_context: Option<&DriverContext>,
) -> DriverResult<String> {
debug!("Executing redis_hget driver");
let host = get_param_string(parameters, "host")?;
let port = get_param_u64(parameters, "port", 6379) as u16;
let password = parameters.get("password").and_then(|v| v.as_str()).unwrap_or("");
let db = get_param_usize(parameters, "db", 0);
let key = get_param_string(parameters, "key")?;
let field = get_param_string(parameters, "field")?;
let mut conn = get_redis_connection(&host, port, password, db)?;
debug!("Getting hash field: {}:{}", key, field);
let value: Option<String> = conn.hget(&key, &field).map_err(|e| DriverError::execution(format!("Failed to get hash field: {}", e)))?;
match value {
Some(v) => {
info!("Successfully retrieved field '{}' from hash '{}'", field, key);
return Ok(v);
}
None => {
info!("Field '{}' not found in hash '{}'", field, key);
return Ok("null".to_string());
}
}
}
}