cadi_core/atomizer/languages/
csharp.rs1use crate::atomizer::{AtomizerConfig, ExtractedAtom, AtomKind};
4use crate::error::CadiResult;
5
6pub struct CSharpAtomizer {
8 _config: AtomizerConfig,
9}
10
11impl CSharpAtomizer {
12 pub fn new(config: AtomizerConfig) -> Self {
13 Self { _config: config }
14 }
15
16 #[cfg(feature = "ast-parsing")]
18 pub fn extract(&self, source: &str) -> CadiResult<Vec<ExtractedAtom>> {
19 use tree_sitter::{Parser, Query, QueryCursor};
20
21 let mut parser = Parser::new();
22 parser.set_language(&tree_sitter_c_sharp::language())?;
23
24 let tree = parser.parse(source, None)
25 .ok_or_else(|| crate::error::CadiError::AtomizerError("Parse failed".into()))?;
26
27 let mut atoms = Vec::new();
28
29 let query_src = r#"
31 (method_declaration
32 name: (identifier) @method_name
33 ) @method
34
35 (class_declaration
36 name: (identifier) @class_name
37 ) @class
38
39 (interface_declaration
40 name: (identifier) @interface_name
41 ) @interface
42
43 (struct_declaration
44 name: (identifier) @struct_name
45 ) @struct
46
47 (enum_declaration
48 name: (identifier) @enum_name
49 ) @enum
50
51 (namespace_declaration
52 name: (_) @namespace_name
53 ) @namespace
54 "#;
55
56 let query = Query::new(&tree_sitter_c_sharp::language(), query_src)?;
57 let mut cursor = QueryCursor::new();
58
59 let matches = cursor.matches(&query, tree.root_node(), source.as_bytes());
60
61 for m in matches {
62 let mut name = "unknown".to_string();
63 let mut kind = AtomKind::Method;
64 let mut atom_node = None;
65
66 for capture in m.captures {
67 let capture_name = query.capture_names()[capture.index as usize];
68 match capture_name {
69 "method_name" | "class_name" | "interface_name" | "struct_name" | "enum_name" | "namespace_name" => {
70 name = capture.node.utf8_text(source.as_bytes()).unwrap_or("unknown").to_string();
71 }
72 "method" => {
73 kind = AtomKind::Method;
74 atom_node = Some(capture.node);
75 }
76 "class" => {
77 kind = AtomKind::Class;
78 atom_node = Some(capture.node);
79 }
80 "interface" => {
81 kind = AtomKind::Interface;
82 atom_node = Some(capture.node);
83 }
84 "struct" => {
85 kind = AtomKind::Struct;
86 atom_node = Some(capture.node);
87 }
88 "enum" => {
89 kind = AtomKind::Enum;
90 atom_node = Some(capture.node);
91 }
92 "namespace" => {
93 kind = AtomKind::Module;
94 atom_node = Some(capture.node);
95 }
96 _ => {}
97 }
98 }
99
100 if let Some(node) = atom_node {
101 let start_byte = node.start_byte();
102 let end_byte = node.end_byte();
103 let start_point = node.start_position();
104 let end_point = node.end_position();
105
106 atoms.push(ExtractedAtom {
107 name,
108 kind,
109 source: source[start_byte..end_byte].to_string(),
110 start_byte,
111 end_byte,
112 start_line: start_point.row + 1,
113 end_line: end_point.row + 1,
114 defines: vec![],
115 references: Vec::new(),
116 doc_comment: None,
117 visibility: crate::atomizer::extractor::Visibility::Public,
118 parent: None,
119 decorators: Vec::new(),
120 });
121 }
122 }
123
124 Ok(atoms)
125 }
126
127 #[cfg(not(feature = "ast-parsing"))]
129 pub fn extract(&self, source: &str) -> CadiResult<Vec<ExtractedAtom>> {
130 use crate::atomizer::AtomExtractor;
131 AtomExtractor::new("csharp", self._config.clone()).extract(source)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use crate::atomizer::AtomizerConfig;
139
140 #[test]
141 fn test_csharp_extraction() {
142 let source = r#"
143 namespace MyNamespace {
144 public class Greeter {
145 public void Greet(string name) {
146 Console.WriteLine("Hello " + name);
147 }
148 }
149 }
150 "#;
151
152 let atomizer = CSharpAtomizer::new(AtomizerConfig::default());
153 let atoms = atomizer.extract(source).unwrap();
154
155 assert!(atoms.iter().any(|a| a.name == "Greeter"));
157 assert!(atoms.iter().any(|a| a.name == "Greet"));
158 }
159}