1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
use crate::ScopeId;
use std::{borrow::Cow, collections::HashMap, sync::Arc};

#[derive(Default, Clone)]
struct Inner {
    // Store a both-way map, memory wise this can be a bit redundant but allows for faster access of information by external libs.
    pub(crate) scope_id_to_details: HashMap<ScopeId, Arc<ScopeDetails>>,
    pub(crate) type_to_scope_id: HashMap<Cow<'static, str>, ScopeId>,
}

/// A collection of scope details containing more information about a recorded profile scope.
#[derive(Default, Clone)]
pub struct ScopeCollection(Inner);

impl ScopeCollection {
    /// Fetches scope details by scope id.
    #[inline]
    pub fn fetch_by_id(&self, scope_id: &ScopeId) -> Option<&Arc<ScopeDetails>> {
        self.0.scope_id_to_details.get(scope_id)
    }

    /// Fetches scope details by scope name.
    #[inline]
    pub fn fetch_by_name(&self, scope_name: &str) -> Option<&ScopeId> {
        self.0.type_to_scope_id.get(scope_name)
    }

    /// Insert a scope into the collection.
    /// This method asserts the scope id is set which only puffin should do.
    /// Custom sinks might use this method to store new scope details received from puffin.
    pub fn insert(&mut self, scope_details: Arc<ScopeDetails>) -> Arc<ScopeDetails> {
        let scope_id = scope_details
            .scope_id
            .expect("`ScopeDetails` missing `ScopeId`");

        self.0
            .type_to_scope_id
            .insert(scope_details.name().clone(), scope_id);
        self.0
            .scope_id_to_details
            .entry(scope_id)
            .or_insert(scope_details)
            .clone()
    }

    /// Fetches all registered scopes and their ids.
    /// Useful for fetching scope id by it's scope name.
    /// For profiler scopes and user scopes this is the manual provided name.
    /// For function profiler scopes this is the function name.
    #[inline]
    pub fn scopes_by_name(&self) -> &HashMap<Cow<'static, str>, ScopeId> {
        &self.0.type_to_scope_id
    }

    /// Fetches all registered scopes.
    /// Useful for fetching scope details by a scope id.
    #[inline]
    pub fn scopes_by_id(&self) -> &HashMap<ScopeId, Arc<ScopeDetails>> {
        &self.0.scope_id_to_details
    }
}

/// Scopes are identified by user-provided name while functions are identified by the function name.
#[derive(Debug, Clone, PartialEq, Hash, PartialOrd, Ord, Eq)]
#[cfg_attr(
    feature = "serialization",
    derive(serde::Serialize, serde::Deserialize)
)]
pub enum ScopeType {
    /// The scope is a function profile scope generated by `puffin::profile_function!`.
    Function,
    /// The named scope is a profile scope inside a function generated by `puffin::profile_scope!` or registered manually.
    /// It is identified by a unique name.
    Named,
}

impl ScopeType {
    /// Returns a string representation of this scope type.
    pub fn type_str(&self) -> &'static str {
        match self {
            ScopeType::Function => "function scope",
            ScopeType::Named => "named",
        }
    }
}

#[derive(Debug, Default, Clone, PartialEq, Hash, PartialOrd, Ord, Eq)]
#[cfg_attr(
    feature = "serialization",
    derive(serde::Serialize, serde::Deserialize)
)]
/// Detailed information about a scope.
pub struct ScopeDetails {
    /// Unique scope identifier.
    /// Always initialized once registered.
    /// It is `None` when an external library has yet to register this scope.
    pub(crate) scope_id: Option<ScopeId>,
    /// A name for a profile scope, a function profile scope does not have a custom provided name.
    pub scope_name: Option<Cow<'static, str>>,
    /// The function name of the function in which this scope is contained.
    /// The name might be slightly modified to represent a short descriptive representation.
    pub function_name: Cow<'static, str>,
    /// The file path in which this scope is contained.
    /// The path might be slightly modified to represent a short descriptive representation.
    pub file_path: Cow<'static, str>,
    /// The exact line number at which this scope is located.
    pub line_nr: u32,
}

impl ScopeDetails {
    /// Creates a new user scope with a unique name.
    pub fn from_scope_name<T>(scope_name: T) -> Self
    where
        T: Into<Cow<'static, str>>,
    {
        Self {
            scope_id: None,
            scope_name: Some(scope_name.into()),
            function_name: Default::default(),
            file_path: Default::default(),
            line_nr: Default::default(),
        }
    }

    /// Creates a new user scope with a unique id allocated by puffin.
    /// This function should not be exposed as only puffin should allocate ids for scopes.
    pub(crate) fn from_scope_id(scope_id: ScopeId) -> Self {
        Self {
            scope_id: Some(scope_id),
            scope_name: None,
            function_name: Default::default(),
            file_path: Default::default(),
            line_nr: Default::default(),
        }
    }

    /// Scope in a function.
    #[inline]
    pub fn with_function_name<T>(mut self, name: T) -> Self
    where
        T: Into<Cow<'static, str>>,
    {
        self.function_name = name.into();
        self
    }

    /// Scope in a file.
    #[inline]
    pub fn with_file<T>(mut self, file: T) -> Self
    where
        T: Into<Cow<'static, str>>,
    {
        self.file_path = file.into();
        self
    }

    /// Scope at a line number.
    #[inline]
    pub fn with_line_nr(mut self, line_nr: u32) -> Self {
        self.line_nr = line_nr;
        self
    }

    /// Returns the scope name if this is a profile scope or else the function name.
    pub fn name(&self) -> &Cow<'static, str> {
        self.scope_name.as_ref().map_or(&self.function_name, |x| x)
    }

    /// Returns what type of scope this is.
    pub fn scope_type(&self) -> ScopeType {
        // scope name is only set for named scopes.
        if self.scope_name.is_some() {
            ScopeType::Named
        } else {
            ScopeType::Function
        }
    }

    /// Returns the exact location of the profile scope formatted as `file:line_nr`
    #[inline]
    pub fn location(&self) -> String {
        if self.line_nr != 0 {
            format!("{}:{}", self.file_path, self.line_nr)
        } else {
            format!("{}", self.file_path)
        }
    }

    // This function should not be exposed as only puffin should allocate ids.
    #[inline]
    pub(crate) fn with_scope_id(mut self, scope_id: ScopeId) -> Self {
        self.scope_id = Some(scope_id);
        self
    }

    // This function should not be exposed as users are supposed to provide scope name in constructor.
    #[inline]
    pub(crate) fn with_scope_name<T>(mut self, scope_name: T) -> Self
    where
        T: Into<Cow<'static, str>>,
    {
        self.scope_name = Some(scope_name.into());
        self
    }
}