pforge_config/
validator.rs1use crate::{ConfigError, ForgeConfig, Result, ToolDef};
2use std::collections::HashSet;
3
4pub fn validate_config(config: &ForgeConfig) -> Result<()> {
5 let mut tool_names = HashSet::new();
7 for tool in &config.tools {
8 let name = tool.name();
9 if !tool_names.insert(name) {
10 return Err(ConfigError::DuplicateToolName(name.to_string()));
11 }
12 }
13
14 for tool in &config.tools {
16 if let ToolDef::Native { handler, .. } = tool {
17 validate_handler_path(&handler.path)?;
18 }
19 }
20
21 Ok(())
22}
23
24fn validate_handler_path(path: &str) -> Result<()> {
25 if path.is_empty() {
26 return Err(ConfigError::InvalidHandlerPath("empty path".to_string()));
27 }
28
29 if !path.contains("::") {
31 return Err(ConfigError::InvalidHandlerPath(format!(
32 "invalid format: {} (expected format: module::function)",
33 path
34 )));
35 }
36
37 Ok(())
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43 use crate::*;
44
45 #[test]
46 fn test_validate_config_success() {
47 let config = ForgeConfig {
48 forge: ForgeMetadata {
49 name: "test".to_string(),
50 version: "1.0.0".to_string(),
51 transport: TransportType::Stdio,
52 optimization: OptimizationLevel::Debug,
53 },
54 tools: vec![
55 ToolDef::Native {
56 name: "tool1".to_string(),
57 description: "Tool 1".to_string(),
58 handler: HandlerRef {
59 path: "module::handler".to_string(),
60 inline: None,
61 },
62 params: ParamSchema {
63 fields: std::collections::HashMap::new(),
64 },
65 timeout_ms: None,
66 },
67 ],
68 resources: vec![],
69 prompts: vec![],
70 state: None,
71 };
72
73 assert!(validate_config(&config).is_ok());
74 }
75
76 #[test]
77 fn test_validate_config_duplicate_tools() {
78 let config = ForgeConfig {
79 forge: ForgeMetadata {
80 name: "test".to_string(),
81 version: "1.0.0".to_string(),
82 transport: TransportType::Stdio,
83 optimization: OptimizationLevel::Debug,
84 },
85 tools: vec![
86 ToolDef::Native {
87 name: "duplicate".to_string(),
88 description: "Tool 1".to_string(),
89 handler: HandlerRef {
90 path: "module::handler1".to_string(),
91 inline: None,
92 },
93 params: ParamSchema {
94 fields: std::collections::HashMap::new(),
95 },
96 timeout_ms: None,
97 },
98 ToolDef::Native {
99 name: "duplicate".to_string(),
100 description: "Tool 2".to_string(),
101 handler: HandlerRef {
102 path: "module::handler2".to_string(),
103 inline: None,
104 },
105 params: ParamSchema {
106 fields: std::collections::HashMap::new(),
107 },
108 timeout_ms: None,
109 },
110 ],
111 resources: vec![],
112 prompts: vec![],
113 state: None,
114 };
115
116 let result = validate_config(&config);
117 assert!(result.is_err());
118 assert!(matches!(result.unwrap_err(), ConfigError::DuplicateToolName(_)));
119 }
120
121 #[test]
122 fn test_validate_handler_path_empty() {
123 let result = validate_handler_path("");
124 assert!(result.is_err());
125 assert!(matches!(result.unwrap_err(), ConfigError::InvalidHandlerPath(_)));
126 }
127
128 #[test]
129 fn test_validate_handler_path_invalid_format() {
130 let result = validate_handler_path("invalid_path");
131 assert!(result.is_err());
132 assert!(matches!(result.unwrap_err(), ConfigError::InvalidHandlerPath(_)));
133 }
134
135 #[test]
136 fn test_validate_handler_path_valid() {
137 let result = validate_handler_path("module::handler");
138 assert!(result.is_ok());
139 }
140}