Skip to main content

sdivi_patterns/queries/
data_access.rs

1//! Node kinds classified as data-access patterns.
2//!
3//! These node kinds correspond to the `data_access` category in the
4//! [`PatternCatalog`](crate::catalog::PatternCatalog).
5
6use std::sync::LazyLock;
7
8use regex::Regex;
9
10/// Tree-sitter node kinds for data-access patterns.
11///
12/// - `call_expression`: function/method calls that access data stores or
13///   external resources (TypeScript/JavaScript/Go: `fetch`, `query`, `read`,
14///   `write`, `db.*`, `sql.*`, etc.). All `call_expression` nodes are
15///   classified here; callee-name narrowing is the consumer's responsibility.
16/// - `call`: Python function calls accessing data (`cursor.*`, `session.*`,
17///   `open`). Same broad-classification rule as above.
18pub const NODE_KINDS: &[&str] = &["call_expression", "call"];
19
20// TypeScript / JavaScript / Go:
21//   ^(fetch|axios)\b  — top-level fetch/axios calls
22//   \b(query|read|write|get|post|put|delete|patch)\(  — method calls by name
23//   \b(db|sql)\.  — db.* and sql.* receiver calls
24//   \.(query|read|write|fetch)\(  — chained method calls
25// ^ anchors the first alternative only; \b guards the rest from false prefixes.
26static TS_JS_GO_RE: LazyLock<Regex> = LazyLock::new(|| {
27    Regex::new(
28        r"^(fetch|axios)\b|\b(query|read|write|get|post|put|delete|patch)\(|\b(db|sql)\.|\.(query|read|write|fetch)\(",
29    )
30    .expect("data_access TS/JS/Go regex is valid")
31});
32
33// Python:
34//   ^ anchors each alternative to the start of the text.
35//   open(  — built-in file open
36//   requests. / httpx. / cursor. / session. / conn.  — common data-access libs
37static PYTHON_RE: LazyLock<Regex> = LazyLock::new(|| {
38    Regex::new(r"^(open\(|requests\.|httpx\.|cursor\.|session\.|conn\.)")
39        .expect("data_access Python regex is valid")
40});
41
42/// Return `true` when `text` looks like a data-access callee for `language`.
43///
44/// Rust and Java always return `false` in v0 — data-access detection for those
45/// languages is library-shaped (`sqlx::query!`, `reqwest::get`) and deferred to
46/// a future regex pass. TypeScript, JavaScript, and Go share one regex table;
47/// Python has its own.
48///
49/// # Examples
50///
51/// ```rust
52/// use sdivi_patterns::queries::data_access::matches_callee;
53///
54/// assert!(matches_callee("fetch(\"/api/users\")", "typescript"));
55/// assert!(matches_callee("cursor.execute(sql)", "python"));
56/// assert!(!matches_callee("Math.max(a, b)", "typescript"));
57/// assert!(!matches_callee("len(x)", "python"));
58/// ```
59pub fn matches_callee(text: &str, language: &str) -> bool {
60    match language {
61        "typescript" | "javascript" | "go" => TS_JS_GO_RE.is_match(text),
62        "python" => PYTHON_RE.is_match(text),
63        _ => false,
64    }
65}