sniff/
roles_surface_contract.rs1use crate::roles::normalize_path;
2use crate::types::FileRecord;
3
4fn looks_like_callback_contract_source(source: &str) -> bool {
5 let lowered = source.to_lowercase();
6 if !lowered.contains("data class") {
7 return false;
8 }
9 if lowered.contains("@composable") || lowered.contains("\nfun ") {
10 return false;
11 }
12
13 let callback_properties = source.matches("-> Unit").count()
14 + source.matches("-> () -> Unit").count()
15 + source.matches("= {}").count()
16 + source.matches("= { _ -> }").count();
17
18 callback_properties >= 3
19}
20
21pub fn is_callback_contract_module(file: &FileRecord) -> bool {
22 let normalized = normalize_path(&file.file_path);
23 let name = crate::roles::file_name(&normalized).to_lowercase();
24
25 let looks_like_kotlin = normalized.ends_with(".kt") || normalized.ends_with(".kts");
26 if !looks_like_kotlin {
27 return false;
28 }
29
30 let name_hint = name.ends_with("callbacks.kt")
31 || name.ends_with("callbacks.kts")
32 || name.ends_with("models.kt")
33 || name.ends_with("bindings.kt");
34
35 name_hint && looks_like_callback_contract_source(&file.source)
36 || looks_like_callback_contract_source(&file.source)
37}