sniff/
roles_surface_protocol.rs1use crate::roles::normalize_path;
2use crate::types::{FileRecord, MethodRecord};
3
4pub fn is_protocol_stub_method(method: &MethodRecord) -> bool {
5 let source = method.source.trim();
6 if source.is_empty() {
7 return false;
8 }
9
10 let mut body_lines = source
11 .lines()
12 .map(str::trim)
13 .filter(|line| !line.is_empty());
14 if let Some(signature) = body_lines.next() {
15 let lowered = signature.to_lowercase();
16 if signature.ends_with("...")
17 || signature.ends_with("pass")
18 || signature.contains(": ...")
19 || lowered.contains(": pass")
20 || lowered.ends_with("raise notimplementederror")
21 || lowered.contains(": raise notimplementederror")
22 {
23 return true;
24 }
25 }
26
27 body_lines.any(|line| {
28 let lowered = line.to_lowercase();
29 line == "..."
30 || line == "pass"
31 || line.ends_with("...")
32 || lowered.ends_with("pass")
33 || lowered.contains(": ...")
34 || lowered.contains(": pass")
35 || lowered.contains("raise notimplementederror")
36 || lowered.contains("return notimplementederror")
37 })
38}
39
40pub fn is_protocol_surface_module(file: &FileRecord) -> bool {
41 let normalized = normalize_path(&file.file_path);
42 if normalized.ends_with("_protocols.py") {
43 return !file.methods.is_empty();
44 }
45
46 if !file.source.contains("Protocol") {
47 return false;
48 }
49
50 if file.methods.is_empty() {
51 return false;
52 }
53
54 file.methods.iter().all(is_protocol_stub_method)
55}