stak_profiler/record/
stack.rs1use crate::{Error, FRAME_SEPARATOR};
2use core::{
3 fmt::{self, Display, Formatter},
4 str::FromStr,
5};
6
7#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub struct Stack {
10 frames: Vec<Option<String>>,
11}
12
13impl Stack {
14 pub const fn new(frames: Vec<Option<String>>) -> Self {
16 Self { frames }
17 }
18
19 pub fn frames(&self) -> impl Iterator<Item = Option<&str>> {
21 self.frames.iter().map(Option::as_deref)
22 }
23
24 pub fn reverse_frames(&mut self) {
26 self.frames.reverse();
27 }
28
29 pub fn collapse_frames(&mut self) {
31 let mut frames = vec![];
32
33 for frame in self.frames.drain(..) {
34 if frames.last() != Some(&frame) {
35 frames.push(frame);
36 }
37 }
38
39 self.frames = frames;
40 }
41
42 pub fn display_local<'a>(&'a self, local_name: &'a str) -> impl Display + 'a {
44 StackDisplay::new(self, local_name)
45 }
46}
47
48impl FromStr for Stack {
49 type Err = Error;
50
51 fn from_str(string: &str) -> Result<Self, Self::Err> {
52 Ok(Self::new(
53 string
54 .split(FRAME_SEPARATOR)
55 .map(|frame| (!frame.is_empty()).then_some(frame.to_owned()))
56 .collect(),
57 ))
58 }
59}
60
61impl Display for Stack {
62 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
63 write!(formatter, "{}", self.display_local(""))
64 }
65}
66
67pub struct StackDisplay<'a> {
68 stack: &'a Stack,
69 local_name: &'a str,
70}
71
72impl<'a> StackDisplay<'a> {
73 pub const fn new(stack: &'a Stack, local_name: &'a str) -> Self {
74 Self { stack, local_name }
75 }
76}
77
78impl Display for StackDisplay<'_> {
79 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
80 let mut first = true;
81
82 for frame in self.stack.frames() {
83 if !first {
84 write!(formatter, "{FRAME_SEPARATOR}")?;
85 }
86
87 first = false;
88
89 write!(formatter, "{}", frame.unwrap_or(self.local_name))?;
90 }
91
92 Ok(())
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use pretty_assertions::assert_eq;
100
101 #[test]
102 fn parse() {
103 let stack = Stack::new(vec![Some("foo".into()), Some("bar".into())]);
104
105 assert_eq!(stack.to_string().parse::<Stack>().unwrap(), stack);
106 }
107
108 #[test]
109 fn display_local() {
110 let stack = Stack::new(vec![Some("foo".into()), None]);
111
112 assert_eq!(stack.to_string(), "foo;");
113 }
114
115 mod collapse_frames {
116 use super::*;
117 use pretty_assertions::assert_eq;
118
119 #[test]
120 fn collapse_named_frames() {
121 let mut stack = Stack::new(vec![
122 Some("foo".into()),
123 Some("foo".into()),
124 Some("foo".into()),
125 ]);
126
127 stack.collapse_frames();
128
129 assert_eq!(stack.to_string(), "foo");
130 }
131
132 #[test]
133 fn collapse_anonymous_frames() {
134 let mut stack = Stack::new(vec![None, None]);
135
136 stack.collapse_frames();
137
138 assert_eq!(stack.to_string(), "");
139 }
140
141 #[test]
142 fn collapse_frames_in_middle() {
143 let mut stack = Stack::new(vec![
144 Some("baz".into()),
145 Some("bar".into()),
146 Some("bar".into()),
147 Some("foo".into()),
148 ]);
149
150 stack.collapse_frames();
151
152 assert_eq!(stack.to_string(), "baz;bar;foo");
153 }
154 }
155}