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
use super::ffi;

/// Debug context property
///
/// This can be used to give a module additional context for debugging panics and crashes by describing key module state by
/// instantiating properties that describe different types of state. For example which level is loaded in a module.
///
/// If there are any panics or crashes in the module during the lifetime of this object then this
/// context will be included in those panic / crash reports.
///
/// Note that this is intended for debugging purposes also and should only be select important state, not describe tons of state in
/// the module. So recommendation is to use max 10 properties or so and update them when they change, not many times per frame / module call.
///
/// # Example
///
/// In this example we load a level, and there can only be a single level active at the time so we can put the debug context property directly inside
/// the object so it the debug context property is active while the level is active.
///
/// ```
/// struct Level {
///     name: String,
///     _debug_prop: DebugContextProperty
/// }
///
/// impl Level {
///     pub fn new(name: String) -> Self {
///         let _debug_prop = DebugContextProperty::new("level", &name)
///         Self {
///             name,
///             _debug_prop
///         }
///     }
/// }
// ```
#[must_use]
#[derive(Debug)]
pub struct DebugContextProperty {
    name: &'static str,
}

impl DebugContextProperty {
    /// Creates a new debug context property
    ///
    /// The name of the property is global and unique to the module, supposed to be brief and is required to be static.
    /// The value of the property can be any string but generally single-line strings are preferred.
    ///
    /// Name and value strings may be capped by the host.
    pub fn new(name: &'static str, value: impl std::fmt::Display) -> Self {
        let mut scope = Self { name };
        scope.push(value);
        scope
    }

    /// Push a new value for an existing debug context property in the same stack.
    pub fn push(&mut self, value: impl std::fmt::Display) {
        let value = value.to_string();
        unsafe {
            ffi::core_v4::core__add_debug_context(
                self.name.as_ptr(),
                self.name.len() as u32,
                value.as_ptr(),
                value.len() as u32,
            );
        }
    }

    /// Pop a value of an existing debug context property stack.
    ///
    /// Does nothing if the property stack was already empty.
    pub fn pop(&mut self) {
        unsafe {
            ffi::core_v4::core__remove_debug_context(self.name.as_ptr(), self.name.len() as u32);
        }
    }

    /// Replace the current value of an existing debug context property (equivalent to pop and
    /// push).
    pub fn reset(&mut self, value: impl std::fmt::Display) {
        self.pop();
        self.push(value);
    }
}

impl Drop for DebugContextProperty {
    fn drop(&mut self) {
        self.pop();
    }
}