cadi_core/atomizer/languages/
python.rs1use crate::atomizer::{AtomizerConfig, ExtractedAtom};
4use crate::error::CadiResult;
5
6pub struct PythonAtomizer {
8 config: AtomizerConfig,
9}
10
11impl PythonAtomizer {
12 pub fn new(config: AtomizerConfig) -> Self {
13 Self { config }
14 }
15
16 pub fn extract(&self, source: &str) -> CadiResult<Vec<ExtractedAtom>> {
18 use crate::atomizer::AtomExtractor;
19 AtomExtractor::new("python", self.config.clone()).extract(source)
20 }
21}
22
23pub struct PythonPatterns;
25
26impl PythonPatterns {
27 pub fn is_test(name: &str, decorators: &[String]) -> bool {
29 name.starts_with("test_")
30 || decorators.iter().any(|d| d.contains("@pytest") || d.contains("@test"))
31 }
32
33 pub fn is_private(name: &str) -> bool {
35 name.starts_with('_') && !name.starts_with("__")
36 }
37
38 pub fn is_dunder(name: &str) -> bool {
40 name.starts_with("__") && name.ends_with("__")
41 }
42
43 pub fn is_dataclass(decorators: &[String]) -> bool {
45 decorators.iter().any(|d| d.contains("@dataclass"))
46 }
47
48 pub fn extract_decorators(source: &str) -> Vec<String> {
50 let mut decorators = Vec::new();
51
52 for line in source.lines() {
53 let trimmed = line.trim();
54 if trimmed.starts_with('@') {
55 decorators.push(trimmed.to_string());
56 } else if !trimmed.is_empty() && !trimmed.starts_with('#') {
57 break;
58 }
59 }
60
61 decorators
62 }
63}