Skip to main content

perl_dap_stack/
visibility.rs

1use crate::StackFrame;
2
3/// Returns true when a frame belongs to debugger/shim internals.
4#[must_use]
5pub fn is_internal_frame_name_and_path(name: &str, path: Option<&str>) -> bool {
6    name.starts_with("Devel::TSPerlDAP::")
7        || name.starts_with("DB::")
8        || path.is_some_and(|value| value.contains("perl5db.pl"))
9}
10
11/// Returns true when a frame belongs to debugger/shim internals.
12#[must_use]
13pub fn is_internal_frame(frame: &StackFrame) -> bool {
14    is_internal_frame_name_and_path(
15        &frame.name,
16        frame.source.as_ref().and_then(|source| source.path.as_deref()),
17    )
18}
19
20/// Filter out internal debugger and shim frames from user-visible stack traces.
21#[must_use]
22pub fn filter_user_visible_frames(frames: Vec<StackFrame>) -> Vec<StackFrame> {
23    frames.into_iter().filter(|frame| !is_internal_frame(frame)).collect()
24}
25
26#[cfg(test)]
27mod tests {
28    use super::{filter_user_visible_frames, is_internal_frame, is_internal_frame_name_and_path};
29    use crate::{Source, StackFrame};
30
31    fn frame(id: i64, name: &str, path: &str) -> StackFrame {
32        StackFrame::new(id, name.to_string(), Some(Source::new(path)), 1)
33    }
34
35    #[test]
36    fn internal_frame_by_name_prefix() {
37        assert!(is_internal_frame(&frame(1, "DB::sub", "/app/main.pl")));
38        assert!(is_internal_frame(&frame(2, "Devel::TSPerlDAP::shim", "/app/main.pl")));
39    }
40
41    #[test]
42    fn internal_frame_by_perl5db_path() {
43        assert!(is_internal_frame(&frame(3, "helper", "/usr/lib/perl5/perl5db.pl")));
44    }
45
46    #[test]
47    fn classification_from_name_and_path() {
48        assert!(is_internal_frame_name_and_path("DB::sub", Some("/app/main.pl")));
49        assert!(is_internal_frame_name_and_path("helper", Some("/usr/lib/perl5/perl5db.pl")));
50        assert!(!is_internal_frame_name_and_path("main::run", Some("/app/main.pl")));
51    }
52
53    #[test]
54    fn filter_keeps_only_user_frames_preserving_order() {
55        let frames = vec![
56            frame(1, "main::start", "/app/main.pl"),
57            frame(2, "DB::sub", "/usr/lib/perl5/perl5db.pl"),
58            frame(3, "Utils::run", "/app/lib/Utils.pm"),
59        ];
60
61        let filtered = filter_user_visible_frames(frames);
62        assert_eq!(filtered.len(), 2);
63        assert_eq!(filtered[0].name, "main::start");
64        assert_eq!(filtered[1].name, "Utils::run");
65    }
66}