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![ToolDef::Native {
55 name: "tool1".to_string(),
56 description: "Tool 1".to_string(),
57 handler: HandlerRef {
58 path: "module::handler".to_string(),
59 inline: None,
60 },
61 params: ParamSchema {
62 fields: std::collections::HashMap::new(),
63 },
64 timeout_ms: None,
65 }],
66 resources: vec![],
67 prompts: vec![],
68 state: None,
69 };
70
71 assert!(validate_config(&config).is_ok());
72 }
73
74 #[test]
75 fn test_validate_config_duplicate_tools() {
76 let config = ForgeConfig {
77 forge: ForgeMetadata {
78 name: "test".to_string(),
79 version: "1.0.0".to_string(),
80 transport: TransportType::Stdio,
81 optimization: OptimizationLevel::Debug,
82 },
83 tools: vec![
84 ToolDef::Native {
85 name: "duplicate".to_string(),
86 description: "Tool 1".to_string(),
87 handler: HandlerRef {
88 path: "module::handler1".to_string(),
89 inline: None,
90 },
91 params: ParamSchema {
92 fields: std::collections::HashMap::new(),
93 },
94 timeout_ms: None,
95 },
96 ToolDef::Native {
97 name: "duplicate".to_string(),
98 description: "Tool 2".to_string(),
99 handler: HandlerRef {
100 path: "module::handler2".to_string(),
101 inline: None,
102 },
103 params: ParamSchema {
104 fields: std::collections::HashMap::new(),
105 },
106 timeout_ms: None,
107 },
108 ],
109 resources: vec![],
110 prompts: vec![],
111 state: None,
112 };
113
114 let result = validate_config(&config);
115 assert!(result.is_err());
116 assert!(matches!(
117 result.unwrap_err(),
118 ConfigError::DuplicateToolName(_)
119 ));
120 }
121
122 #[test]
123 fn test_validate_handler_path_empty() {
124 let result = validate_handler_path("");
125 assert!(result.is_err());
126 assert!(matches!(
127 result.unwrap_err(),
128 ConfigError::InvalidHandlerPath(_)
129 ));
130 }
131
132 #[test]
133 fn test_validate_handler_path_invalid_format() {
134 let result = validate_handler_path("invalid_path");
135 assert!(result.is_err());
136 assert!(matches!(
137 result.unwrap_err(),
138 ConfigError::InvalidHandlerPath(_)
139 ));
140 }
141
142 #[test]
143 fn test_validate_handler_path_valid() {
144 let result = validate_handler_path("module::handler");
145 assert!(result.is_ok());
146 }
147}