Skip to main content

sniff/
roles_surface.rs

1#[path = "roles_surface_assignment.rs"]
2mod assignment;
3#[path = "roles_surface_config.rs"]
4mod config;
5#[path = "roles_surface_contract.rs"]
6mod contract;
7#[path = "roles_surface_facade.rs"]
8mod facade;
9#[path = "roles_surface_hooks.rs"]
10mod hooks;
11#[path = "roles_surface_presentation.rs"]
12mod presentation;
13#[path = "roles_surface_protocol.rs"]
14mod protocol;
15
16use crate::types::FileRecord;
17
18fn source_has_export_markers(source: &str) -> bool {
19    let lower = source.to_lowercase();
20    (lower.contains("from ") && lower.contains(" import "))
21        || lower.contains("export * from")
22        || lower.contains("export {")
23        || lower.contains("pub use ")
24        || lower.contains("__all__")
25        || lower.contains(" as _")
26}
27
28fn source_has_only_exports(source: &str) -> bool {
29    let lower = source.to_lowercase();
30    source_has_export_markers(source) && !lower.contains("def ") && !lower.contains("class ")
31}
32
33fn source_has_only_module_declarations(source: &str) -> bool {
34    let mut saw_declaration = false;
35
36    for line in source.lines() {
37        let trimmed = line.trim();
38        if trimmed.is_empty()
39            || trimmed.starts_with("//")
40            || trimmed.starts_with("/*")
41            || trimmed.starts_with('*')
42        {
43            continue;
44        }
45
46        let is_module_declaration = trimmed.starts_with("mod ")
47            || trimmed.starts_with("pub mod ")
48            || (trimmed.starts_with("pub(")
49                && (trimmed.contains(" mod ") || trimmed.contains(" use ")));
50        let is_use_declaration = trimmed.starts_with("use ")
51            || trimmed.starts_with("pub use ")
52            || (trimmed.starts_with("pub(") && trimmed.contains(" use "));
53        let is_attribute = trimmed.starts_with("#![") || trimmed.starts_with("#[");
54
55        if is_module_declaration || is_use_declaration || is_attribute {
56            saw_declaration = true;
57            continue;
58        }
59
60        return false;
61    }
62
63    saw_declaration
64}
65
66pub fn source_looks_like_wrapper_module(source: &str) -> bool {
67    source_has_export_markers(source)
68}
69
70pub fn is_pure_reexport_module(file: &FileRecord) -> bool {
71    source_has_only_exports(&file.source)
72        || (file.methods.is_empty()
73            && source_has_export_markers(&file.source)
74            && !file.source.to_lowercase().contains("def "))
75}
76
77pub fn is_module_barrel_module(file: &FileRecord) -> bool {
78    file.methods.is_empty() && source_has_only_module_declarations(&file.source)
79}
80
81pub fn is_wrapper_only_module(file: &FileRecord) -> bool {
82    if file.methods.is_empty() {
83        return false;
84    }
85
86    file.methods.iter().all(|method| {
87        facade::is_thin_wrapper_export(method) || facade::is_thin_delegation_method(method)
88    })
89}
90
91pub use assignment::is_wrapper_assignment;
92pub use config::is_config_validation_module;
93pub use contract::is_callback_contract_module;
94pub use facade::{is_detector_facade_module, is_thin_delegation_method, is_thin_wrapper_export};
95pub use hooks::is_hydration_hook_module;
96pub use presentation::is_presentation_surface_module;
97pub use protocol::{is_protocol_stub_method, is_protocol_surface_module};