use tree_sitter::Node;
use super::qualifiers::Scope;
use super::refs::type_descriptor;
use super::text::{named_children, text};
use super::types::HandlerRegistrationRecord;
use std::collections::HashSet;
const INSTALL_PREFIXES: &[&str] = &["Add", "Register", "Use", "Map", "Subscribe"];
const ROLE_SUFFIXES: &[&str] = &[
"Consumer",
"Handler",
"Processor",
"Worker",
"Saga",
"Subscriber",
"Listener",
];
fn is_installation_name(name: &str) -> bool {
INSTALL_PREFIXES.iter().any(|p| name.starts_with(p))
|| ROLE_SUFFIXES.iter().any(|s| name.ends_with(s))
}
pub(super) fn handler_registration_fact(
function: Node,
ns: &str,
src: &[u8],
scope: &Scope,
) -> Option<HandlerRegistrationRecord> {
let name_node = function.child_by_field_name("name")?;
if name_node.kind() != "generic_name" {
return None;
}
let ident = named_children(name_node)
.into_iter()
.find(|c| c.kind() == "identifier")?;
if !is_installation_name(&text(ident, src)) {
return None;
}
let list = named_children(name_node)
.into_iter()
.find(|c| c.kind() == "type_argument_list")?;
let args = named_children(list);
let [only] = args.as_slice() else {
return None;
};
let handler = type_descriptor(Some(*only), src, &scope.type_params);
if handler == "*" || handler == "?" {
return None;
}
Some(HandlerRegistrationRecord {
handler,
namespace: ns.to_string(),
line: function.start_position().row + 1,
})
}
pub(super) fn property_message_args(
node: Node,
src: &[u8],
type_params: &HashSet<String>,
) -> Vec<String> {
let Some(body) = named_children(node)
.into_iter()
.find(|c| c.kind() == "declaration_list")
else {
return Vec::new();
};
let mut out: Vec<String> = Vec::new();
let mut seen: HashSet<String> = HashSet::new();
for member in named_children(body) {
if member.kind() != "property_declaration" {
continue;
}
let Some(arg) = single_type_argument(member.child_by_field_name("type"), src, type_params)
else {
continue;
};
if seen.insert(arg.clone()) {
out.push(arg);
}
}
out
}
fn single_type_argument(
type_node: Option<Node>,
src: &[u8],
type_params: &HashSet<String>,
) -> Option<String> {
let node = type_node?;
if node.kind() != "generic_name" {
return None;
}
let list = named_children(node)
.into_iter()
.find(|c| c.kind() == "type_argument_list")?;
let args = named_children(list);
let [only] = args.as_slice() else {
return None;
};
let descriptor = type_descriptor(Some(*only), src, type_params);
if descriptor == "*" || descriptor == "?" {
return None;
}
Some(descriptor)
}