codegraph_python/
config.rs

1/// Configuration for Python parser behavior
2#[derive(Debug, Clone)]
3pub struct ParserConfig {
4    /// Include private entities (names starting with _)
5    pub include_private: bool,
6
7    /// Include test functions (names starting with test_)
8    pub include_tests: bool,
9
10    /// Parse and extract docstrings
11    pub parse_docs: bool,
12
13    /// Maximum file size in bytes (files larger than this will be skipped)
14    pub max_file_size: usize,
15
16    /// File extensions to parse (default: [".py"])
17    pub file_extensions: Vec<String>,
18
19    /// Directories to exclude from project parsing
20    pub exclude_dirs: Vec<String>,
21
22    /// Enable parallel processing for multi-file projects
23    pub parallel: bool,
24
25    /// Number of threads for parallel processing (None = use default)
26    pub num_threads: Option<usize>,
27}
28
29impl Default for ParserConfig {
30    fn default() -> Self {
31        Self {
32            include_private: true,
33            include_tests: true,
34            parse_docs: true,
35            max_file_size: 10 * 1024 * 1024, // 10MB default
36            file_extensions: vec![".py".to_string()],
37            exclude_dirs: vec![
38                "__pycache__".to_string(),
39                ".git".to_string(),
40                ".venv".to_string(),
41                "venv".to_string(),
42                "env".to_string(),
43                ".tox".to_string(),
44                "dist".to_string(),
45                "build".to_string(),
46                ".eggs".to_string(),
47                "*.egg-info".to_string(),
48            ],
49            parallel: false,
50            num_threads: None,
51        }
52    }
53}
54
55impl ParserConfig {
56    /// Validate the configuration
57    pub fn validate(&self) -> Result<(), String> {
58        if let Some(threads) = self.num_threads {
59            if threads == 0 {
60                return Err("num_threads must be greater than 0".to_string());
61            }
62        }
63
64        if self.max_file_size == 0 {
65            return Err("max_file_size must be greater than 0".to_string());
66        }
67
68        if self.file_extensions.is_empty() {
69            return Err("file_extensions cannot be empty".to_string());
70        }
71
72        Ok(())
73    }
74
75    /// Check if a file extension should be parsed
76    pub fn should_parse_extension(&self, extension: &str) -> bool {
77        self.file_extensions.iter().any(|ext| {
78            let ext = ext.trim_start_matches('.');
79            extension.trim_start_matches('.') == ext
80        })
81    }
82
83    /// Check if a directory should be excluded
84    pub fn should_exclude_dir(&self, dir_name: &str) -> bool {
85        self.exclude_dirs.iter().any(|excluded| {
86            // Handle glob patterns like *.egg-info
87            if excluded.contains('*') {
88                let pattern = excluded.replace('*', "");
89                dir_name.contains(&pattern)
90            } else {
91                dir_name == excluded
92            }
93        })
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_default_config() {
103        let config = ParserConfig::default();
104        assert!(config.include_private);
105        assert!(config.include_tests);
106        assert!(config.parse_docs);
107        assert_eq!(config.max_file_size, 10 * 1024 * 1024);
108        assert!(!config.parallel);
109    }
110
111    #[test]
112    fn test_validate() {
113        let mut config = ParserConfig::default();
114        assert!(config.validate().is_ok());
115
116        config.num_threads = Some(0);
117        assert!(config.validate().is_err());
118
119        config.num_threads = Some(4);
120        assert!(config.validate().is_ok());
121    }
122
123    #[test]
124    fn test_should_parse_extension() {
125        let config = ParserConfig::default();
126        assert!(config.should_parse_extension(".py"));
127        assert!(config.should_parse_extension("py"));
128        assert!(!config.should_parse_extension(".rs"));
129    }
130
131    #[test]
132    fn test_should_exclude_dir() {
133        let config = ParserConfig::default();
134        assert!(config.should_exclude_dir("__pycache__"));
135        assert!(config.should_exclude_dir(".venv"));
136        assert!(config.should_exclude_dir("mypackage.egg-info"));
137        assert!(!config.should_exclude_dir("src"));
138    }
139}