pforge_config/
validator.rs

1use crate::{ConfigError, ForgeConfig, Result, ToolDef};
2use std::collections::HashSet;
3
4pub fn validate_config(config: &ForgeConfig) -> Result<()> {
5    // Check for duplicate tool names
6    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    // Validate handler paths for native tools
15    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    // Basic validation: should contain ::
30    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}