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![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}