Skip to content

Commit b59e0e5

Browse files
committed
refactor: package reestructure and modularization
1 parent b7dd532 commit b59e0e5

10 files changed

Lines changed: 543 additions & 457 deletions

File tree

src/finder/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
pub mod python;
2+
3+
use std::{fs, path::Path};
4+
5+
use sqlparser;
6+
7+
pub struct QueryInCode {
8+
pub line: usize,
9+
pub statements: Vec<sqlparser::ast::Statement>,
10+
}
11+
12+
pub const SUPPORTED_CODE_FILE_EXTENSIONS: [&str; 1] = ["py"];
13+
14+
pub fn find_queries_in_code(file_path: &Path) -> Result<Vec<super::QueryInCode>, String> {
15+
let code = fs::read(file_path);
16+
let file_extension = &file_path.extension().unwrap().to_string_lossy();
17+
18+
match code {
19+
Ok(code) => match file_extension.as_ref() {
20+
"py" => python::find_queries_in_code(&code),
21+
_ => Err(format!("File not supported {file_extension}")),
22+
},
23+
Err(err) => Err(format!(
24+
"Could not open {:?} due to error: {err}",
25+
file_path
26+
)),
27+
}
28+
}

src/finder/python.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
pub fn find_queries_in_code(code: &[u8]) -> Result<Vec<super::QueryInCode>, String> {
2+
let mut parser = tree_sitter::Parser::new();
3+
4+
parser
5+
.set_language(tree_sitter_python::language())
6+
.expect("Error loading Python grammar");
7+
8+
let parsed: Option<tree_sitter::Tree> = parser.parse(code, None);
9+
10+
let mut queries: Vec<super::QueryInCode> = Vec::new();
11+
12+
if let Some(tree) = parsed {
13+
find_queries_in_tree(&tree.root_node(), &code, &mut queries, None);
14+
return Ok(queries);
15+
} else {
16+
return Err("Could not parse code".to_string());
17+
}
18+
}
19+
20+
fn find_queries_in_tree(
21+
node: &tree_sitter::Node,
22+
code: &[u8],
23+
queries: &mut Vec<super::QueryInCode>,
24+
verbose: Option<u8>,
25+
) {
26+
let mut cursor = node.walk();
27+
let dialect = sqlparser::dialect::GenericDialect {};
28+
29+
for child in node.children(&mut cursor) {
30+
let mut child_cursor = child.walk();
31+
32+
for component in child.children(&mut child_cursor) {
33+
if component.kind() != "assignment" {
34+
continue;
35+
}
36+
37+
if component.child_count() > 3 {
38+
continue;
39+
}
40+
41+
let identifier = component.child(0).unwrap();
42+
let equal = component.child(1).unwrap();
43+
let var = component.child(2).unwrap();
44+
45+
let is_string_assignment =
46+
identifier.kind() == "identifier" && equal.kind() == "=" && var.kind() == "string";
47+
48+
if !is_string_assignment {
49+
continue;
50+
}
51+
52+
let mut content_as_string = String::new();
53+
54+
let mut var_cursor = var.walk();
55+
for var_component in var.children(&mut var_cursor) {
56+
if var_component.kind() == "string_content" {
57+
content_as_string.push_str(&String::from_utf8_lossy(
58+
&code[var_component.start_byte()..var_component.end_byte()],
59+
));
60+
}
61+
if var_component.kind() == "interpolation" {
62+
// replace any interpolation in fstring with 1 will always produce valid parsable sql
63+
// when they are inplace of static values
64+
content_as_string.push_str("1");
65+
}
66+
}
67+
68+
// ! Duct tape
69+
if content_as_string.find("REPLACE").is_some() {
70+
continue;
71+
}
72+
// !
73+
74+
let query_at = component.start_position();
75+
76+
let statements = sqlparser::parser::Parser::parse_sql(&dialect, &content_as_string);
77+
78+
match statements {
79+
Ok(statements) => {
80+
queries.push(super::QueryInCode {
81+
line: query_at.row + 1,
82+
statements,
83+
});
84+
}
85+
Err(err) => {
86+
if verbose.unwrap_or(0) > 0 {
87+
eprintln!("{err} {content_as_string}");
88+
}
89+
}
90+
}
91+
}
92+
93+
find_queries_in_tree(&child, code, queries, None);
94+
}
95+
}

0 commit comments

Comments
 (0)