use super::tree::{AstNode, ParsedFile};
use crate::diagnostic::Language;
const PREDICATE_VERBS: &[&str] = &["contains", "starts_with", "ends_with", "matches"];
const PREDICATE_PREFIXES: &[&str] = &[
"is", "has", "can", "should", "supports", "allows", "requires", "needs",
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReturnShape {
Unit,
Boolean,
PositionalTuple {
arity: usize,
},
Unknown,
Other,
}
#[derive(Clone)]
pub struct FunctionReturn<'a> {
pub name: String,
pub name_node: AstNode<'a>,
pub declared_type: Option<AstNode<'a>>,
pub shape: ReturnShape,
}
impl FunctionReturn<'_> {
#[must_use]
pub fn declared_type_text(&self) -> String {
self.declared_type.map_or_else(
|| {
match self.name_node.lang() {
Language::Python => "None",
Language::Rust => "()",
}
.to_owned()
},
|declared_type| declared_type.text().into_owned(),
)
}
}
#[must_use]
pub fn collect_function_returns(file: &ParsedFile) -> Vec<FunctionReturn<'_>> {
dispatch_lang!(file.lang(), collect_function_returns(file))
}
#[must_use]
pub fn is_predicate_name(name: &str) -> bool {
let bare = name.trim_start_matches('_');
PREDICATE_VERBS.contains(&bare)
|| PREDICATE_VERBS
.iter()
.chain(PREDICATE_PREFIXES)
.any(|prefix| {
bare.strip_prefix(prefix)
.and_then(|rest| rest.strip_prefix('_'))
.is_some_and(|rest| !rest.is_empty())
})
}