use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
const MAX_SERVER_ID_LENGTH: usize = 64;
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct GenerateSkillParams {
pub server_id: String,
pub servers_dir: Option<PathBuf>,
pub skill_name: Option<String>,
pub use_case_hints: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct GenerateSkillResult {
pub server_id: String,
pub skill_name: String,
pub server_description: Option<String>,
pub categories: Vec<SkillCategory>,
pub tool_count: usize,
pub example_tools: Vec<ToolExample>,
pub generation_prompt: String,
pub output_path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SkillCategory {
pub name: String,
pub display_name: String,
pub tools: Vec<SkillTool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SkillTool {
pub name: String,
pub typescript_name: String,
pub description: String,
pub keywords: Vec<String>,
pub required_params: Vec<String>,
pub optional_params: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ToolExample {
pub tool_name: String,
pub description: String,
pub cli_command: String,
pub params_json: String,
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct SaveSkillParams {
pub server_id: String,
pub content: String,
pub output_path: Option<PathBuf>,
#[serde(default)]
pub overwrite: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SaveSkillResult {
pub success: bool,
pub output_path: String,
pub overwritten: bool,
pub metadata: SkillMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SkillMetadata {
pub name: String,
pub description: String,
pub section_count: usize,
pub word_count: usize,
}
pub fn validate_server_id(server_id: &str) -> Result<(), String> {
if server_id.len() > MAX_SERVER_ID_LENGTH {
return Err(format!(
"server_id too long: {} chars exceeds {} limit",
server_id.len(),
MAX_SERVER_ID_LENGTH
));
}
if !server_id
.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
{
return Err(
"server_id must contain only lowercase letters, digits, and hyphens".to_string(),
);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_server_id_valid() {
assert!(validate_server_id("github").is_ok());
assert!(validate_server_id("my-server").is_ok());
assert!(validate_server_id("server123").is_ok());
assert!(validate_server_id("my-server-123").is_ok());
}
#[test]
fn test_validate_server_id_uppercase() {
let result = validate_server_id("GitHub");
assert!(result.is_err());
assert!(result.unwrap_err().contains("lowercase"));
}
#[test]
fn test_validate_server_id_underscore() {
let result = validate_server_id("my_server");
assert!(result.is_err());
assert!(result.unwrap_err().contains("lowercase"));
}
#[test]
fn test_validate_server_id_special_chars() {
let result = validate_server_id("my@server");
assert!(result.is_err());
assert!(result.unwrap_err().contains("lowercase"));
}
#[test]
fn test_validate_server_id_too_long() {
let long_id = "a".repeat(65);
let result = validate_server_id(&long_id);
assert!(result.is_err());
assert!(result.unwrap_err().contains("too long"));
}
#[test]
fn test_validate_server_id_max_length() {
let max_id = "a".repeat(64);
assert!(validate_server_id(&max_id).is_ok());
}
}