Skip to main content

dear_imgui_rs/utils/
logging.rs

1use crate::sys;
2
3/// Auto-open depth for Dear ImGui logging helpers.
4///
5/// Dear ImGui uses `-1` to mean "use the configured default depth". This type keeps that sentinel
6/// out of the safe Rust API while still allowing an explicit non-negative tree depth.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct LogAutoOpenDepth(Option<u32>);
9
10impl LogAutoOpenDepth {
11    /// Use Dear ImGui's configured default log auto-open depth.
12    pub const DEFAULT: Self = Self(None);
13
14    /// Create an explicit non-negative auto-open depth.
15    ///
16    /// Panics if `depth` exceeds Dear ImGui's signed `int` range.
17    #[inline]
18    pub const fn new(depth: u32) -> Self {
19        assert!(
20            depth <= i32::MAX as u32,
21            "LogAutoOpenDepth::new() depth exceeded i32::MAX"
22        );
23        Self(Some(depth))
24    }
25
26    #[inline]
27    pub(crate) const fn raw(self) -> i32 {
28        match self.0 {
29            Some(depth) => depth as i32,
30            None => -1,
31        }
32    }
33}
34
35impl From<u32> for LogAutoOpenDepth {
36    fn from(depth: u32) -> Self {
37        Self::new(depth)
38    }
39}
40
41impl crate::ui::Ui {
42    /// Start logging to TTY.
43    #[doc(alias = "LogToTTY")]
44    pub fn log_to_tty(&self, auto_open_depth: impl Into<LogAutoOpenDepth>) {
45        self.run_with_bound_context(|| unsafe { sys::igLogToTTY(auto_open_depth.into().raw()) });
46    }
47
48    /// Start logging to file with the default filename.
49    #[doc(alias = "LogToFile")]
50    pub fn log_to_file_default(&self, auto_open_depth: impl Into<LogAutoOpenDepth>) {
51        self.run_with_bound_context(|| unsafe {
52            sys::igLogToFile(auto_open_depth.into().raw(), std::ptr::null())
53        });
54    }
55
56    /// Start logging to file.
57    ///
58    /// # Errors
59    ///
60    /// Returns an error if `filename` contains NUL bytes.
61    #[doc(alias = "LogToFile")]
62    pub fn log_to_file(
63        &self,
64        auto_open_depth: impl Into<LogAutoOpenDepth>,
65        filename: &std::path::Path,
66    ) -> crate::error::ImGuiResult<()> {
67        use crate::error::SafeStringConversion;
68        let cstr = filename.to_string_lossy().into_owned().to_cstring_safe()?;
69        self.run_with_bound_context(|| unsafe {
70            sys::igLogToFile(auto_open_depth.into().raw(), cstr.as_ptr())
71        });
72        Ok(())
73    }
74
75    /// Start logging to clipboard.
76    #[doc(alias = "LogToClipboard")]
77    pub fn log_to_clipboard(&self, auto_open_depth: impl Into<LogAutoOpenDepth>) {
78        self.run_with_bound_context(|| unsafe {
79            sys::igLogToClipboard(auto_open_depth.into().raw())
80        });
81    }
82
83    /// Show ImGui's logging buttons (TTY/File/Clipboard).
84    #[doc(alias = "LogButtons")]
85    pub fn log_buttons(&self) {
86        self.run_with_bound_context(|| unsafe { sys::igLogButtons() });
87    }
88
89    /// Finish logging (close file / copy to clipboard as needed).
90    #[doc(alias = "LogFinish")]
91    pub fn log_finish(&self) {
92        self.run_with_bound_context(|| unsafe { sys::igLogFinish() });
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    #[test]
99    fn log_auto_open_depth_preserves_default_sentinel_and_rejects_overflow() {
100        assert_eq!(super::LogAutoOpenDepth::DEFAULT.raw(), -1);
101        assert_eq!(super::LogAutoOpenDepth::new(0).raw(), 0);
102        assert_eq!(super::LogAutoOpenDepth::new(3).raw(), 3);
103        assert!(
104            std::panic::catch_unwind(|| super::LogAutoOpenDepth::new(i32::MAX as u32 + 1)).is_err()
105        );
106    }
107}