ittapi 0.5.0

High-level Rust bindings for ittapi
Documentation
use std::ffi::CString;

/// See the [Event API] documentation.
///
/// [Event API]: https://www.intel.com/content/www/us/en/docs/vtune-profiler/user-guide/current/event-api.html
///
/// ```
/// let event = ittapi::Event::new("foo");
/// {
///   let span = event.start();
///   // do some work...
///   // ...the event is stopped when `span` is dropped
/// }
/// ```
pub struct Event(ittapi_sys::__itt_event);
impl Event {
    /// Create the event.
    ///
    /// # Panics
    ///
    /// Panics if the name contains a `0` byte or if its size will not fit in a 32-bit
    /// representation.
    #[must_use]
    pub fn new(name: &str) -> Self {
        #[cfg(unix)]
        let create_fn = unsafe { ittapi_sys::__itt_event_create_ptr__3_0 };
        #[cfg(windows)]
        let create_fn = unsafe { ittapi_sys::__itt_event_createA_ptr__3_0 };
        if let Some(create_fn) = create_fn {
            let c_string =
                CString::new(name).expect("unable to create a CString; does it contain a 0 byte?");
            let size = name
                .len()
                .try_into()
                .expect("unable to fit the name length into an i32");
            let event = unsafe { create_fn(c_string.as_ptr(), size) };
            Self(event)
        } else {
            Self(-1)
        }
    }

    /// Start the event.
    ///
    /// # Panics
    ///
    /// This will panic if the ITT library cannot start the event.
    #[must_use]
    pub fn start(&self) -> StartedEvent {
        if let Some(start_fn) = unsafe { ittapi_sys::__itt_event_start_ptr__3_0 } {
            let result = unsafe { start_fn(self.0) };
            assert!(result == 0, "unable to start event");
        }
        StartedEvent { event: self.0 }
    }
}

/// Contains the information about a started event.
///
/// Allows for the drop implementation to end the event when it goes out of scope.
pub struct StartedEvent {
    event: ittapi_sys::__itt_event,
}

impl StartedEvent {
    /// End the event.
    #[allow(clippy::unused_self)]
    pub fn end(self) {
        // Do nothing; the `Drop` implementation does the work. See discussion at
        // https://stackoverflow.com/questions/53254645.
    }
}

impl Drop for StartedEvent {
    fn drop(&mut self) {
        if let Some(end_fn) = unsafe { ittapi_sys::__itt_event_end_ptr__3_0 } {
            let result = unsafe { end_fn(self.event) };
            assert!(result == 0, "unable to stop event");
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn sanity() {
        let event = Event::new("test");
        let _started_event = event.start();
        // Dropping `started_event` ends the event.
    }

    #[test]
    fn double_end() {
        let event = Event::new("test");
        let s1 = event.start();
        let s2 = event.start();
        s2.end();
        s1.end();
    }
}