the_code_graph_parser/
lib.rs1#![forbid(unsafe_code)]
2
3mod go;
4mod python;
5mod registry;
6pub mod resolver;
7mod rust_lang;
8mod typescript;
9
10#[cfg(test)]
11pub mod test_utils;
12
13pub use go::GoParser;
14pub use python::PythonParser;
15pub use registry::ParserRegistry;
16pub use rust_lang::RustParser;
17pub use typescript::{JavaScriptParser, TypeScriptParser};
18
19use domain::model::{Edge, SymbolNode};
20use std::path::Path;
21
22#[derive(Debug, Clone, Default)]
29pub struct ParseResult {
30 pub symbols: Vec<SymbolNode>,
31 pub edges: Vec<Edge>,
32 pub imports: Vec<RawImport>,
33 pub exports: Vec<Export>,
34}
35
36#[derive(Debug, Clone, Default)]
39pub struct RawImport {
40 pub specifier: String,
41 pub names: Vec<ImportName>,
42 pub is_type_only: bool,
43 pub is_side_effect: bool,
44 pub is_namespace: bool,
45 pub line: usize,
46}
47
48#[derive(Debug, Clone)]
50pub struct ImportName {
51 pub name: String,
52 pub alias: Option<String>,
53 pub is_type: bool,
54}
55
56#[derive(Debug, Clone, Default)]
59pub struct Export {
60 pub name: String,
61 pub local_name: Option<String>,
62 pub is_default: bool,
63 pub is_type_only: bool,
64 pub is_reexport: bool,
65 pub source_specifier: Option<String>,
66}
67
68pub trait LanguageParser: Send + Sync {
75 fn language(&self) -> domain::model::Language;
77
78 fn file_extensions(&self) -> &[&str];
80
81 fn parse(&self, source: &[u8], path: &Path) -> domain::error::Result<ParseResult>;
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn parse_result_default_is_empty() {
92 let result = ParseResult::default();
93 assert!(result.symbols.is_empty());
94 assert!(result.edges.is_empty());
95 assert!(result.imports.is_empty());
96 assert!(result.exports.is_empty());
97 }
98
99 #[test]
100 fn raw_import_default_values() {
101 let imp = RawImport::default();
102 assert!(imp.specifier.is_empty());
103 assert!(imp.names.is_empty());
104 assert!(!imp.is_type_only);
105 assert!(!imp.is_side_effect);
106 assert!(!imp.is_namespace);
107 assert_eq!(imp.line, 0);
108 }
109
110 #[test]
111 fn export_default_values() {
112 let exp = Export::default();
113 assert!(exp.name.is_empty());
114 assert!(exp.local_name.is_none());
115 assert!(!exp.is_default);
116 assert!(!exp.is_type_only);
117 assert!(!exp.is_reexport);
118 assert!(exp.source_specifier.is_none());
119 }
120
121 #[test]
122 fn import_name_construction() {
123 let name = ImportName {
124 name: "foo".into(),
125 alias: Some("bar".into()),
126 is_type: true,
127 };
128 assert_eq!(name.name, "foo");
129 assert_eq!(name.alias.as_deref(), Some("bar"));
130 assert!(name.is_type);
131 }
132
133 #[test]
134 fn parse_result_can_hold_symbols_and_edges() {
135 use domain::model::*;
136
137 let sym = SymbolNode {
138 name: "foo".into(),
139 qualified_name: "test.ts::foo".into(),
140 kind: SymbolKind::Function,
141 location: Location {
142 file: "test.ts".into(),
143 line_start: 1,
144 line_end: 3,
145 col_start: 0,
146 col_end: 1,
147 },
148 visibility: Visibility::Public,
149 is_exported: true,
150 is_async: false,
151 is_test: false,
152 decorators: vec![],
153 signature: None,
154 };
155
156 let edge = Edge {
157 kind: EdgeKind::Contains,
158 source: "test.ts".into(),
159 target: "test.ts::foo".into(),
160 metadata: None,
161 };
162
163 let result = ParseResult {
164 symbols: vec![sym],
165 edges: vec![edge],
166 imports: vec![],
167 exports: vec![],
168 };
169 assert_eq!(result.symbols.len(), 1);
170 assert_eq!(result.edges.len(), 1);
171 }
172}