sqry_lang_java/relations/
java_common.rs1use sqry_lang_support::relations::{build_qualified_name, normalize_type_signature};
5use std::path::{Path, PathBuf};
6use tree_sitter::{Node, Tree};
7
8#[derive(Debug)]
10pub struct JavaRelationContext<'a> {
11 pub file: &'a Path,
13 pub package_name: Option<String>,
15}
16
17impl<'a> JavaRelationContext<'a> {
18 #[must_use]
20 pub fn new(file: &'a Path, package_name: Option<String>) -> Self {
21 Self { file, package_name }
22 }
23}
24
25#[derive(Debug, Clone)]
27pub struct PackageResolver {
28 source_root: PathBuf,
29}
30
31impl PackageResolver {
32 pub fn new(source_root: impl Into<PathBuf>) -> Self {
34 Self {
35 source_root: source_root.into(),
36 }
37 }
38
39 #[must_use]
41 pub fn package_to_path(&self, package_name: &str) -> PathBuf {
42 let relative = package_name.replace('.', std::path::MAIN_SEPARATOR_STR);
43 self.source_root.join(relative)
44 }
45
46 #[must_use]
48 pub fn package_from_ast(tree: &Tree, content: &[u8]) -> Option<String> {
49 let root = tree.root_node();
50 let mut cursor = root.walk();
51 for child in root.named_children(&mut cursor) {
52 if child.kind() != "package_declaration" {
53 continue;
54 }
55 if let Some(candidate) = extract_package_name(child, content) {
56 return Some(candidate);
57 }
58 }
59 None
60 }
61}
62
63fn extract_package_name(node: Node<'_>, content: &[u8]) -> Option<String> {
64 if let Some(candidate) = node
65 .child_by_field_name("name")
66 .and_then(|name_node| extract_package_candidate(name_node, content))
67 {
68 return Some(candidate);
69 }
70
71 let mut name_cursor = node.walk();
72 node.named_children(&mut name_cursor)
73 .filter(|child| matches!(child.kind(), "scoped_identifier" | "identifier"))
74 .find_map(|child| extract_package_candidate(child, content))
75}
76
77fn extract_package_candidate(node: Node<'_>, content: &[u8]) -> Option<String> {
78 node.utf8_text(content)
79 .ok()
80 .and_then(normalize_package_name)
81}
82
83fn normalize_package_name(text: &str) -> Option<String> {
84 let candidate: String = text.chars().filter(|c| !c.is_whitespace()).collect();
85 if candidate.is_empty() {
86 None
87 } else {
88 Some(candidate)
89 }
90}
91
92#[must_use]
94pub fn build_member_symbol(package: Option<&str>, class_stack: &[String], member: &str) -> String {
95 build_qualified_name(
96 package,
97 class_stack.iter().map(std::string::String::as_str),
98 member,
99 )
100}
101
102#[must_use]
104pub fn build_symbol(package: Option<&str>, class_stack: &[String], name: &str) -> String {
105 build_qualified_name(
106 package,
107 class_stack.iter().map(std::string::String::as_str),
108 name,
109 )
110}
111
112#[must_use]
114pub fn normalize_type_name(node: &Node, content: &[u8]) -> String {
115 let raw = node.utf8_text(content).unwrap_or_default();
116 normalize_type_signature(raw)
117}