Skip to main content

sniff/
roles_surface_protocol.rs

1use 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}