mdflib 0.2.1

Rust bindings for mdflib
Documentation
//! ChannelGroup wrapper for mdflib IChannelGroup
//!
//! A channel group is a collection of channels that are sampled at the same time.
//! This module provides access to the channel group's metadata, such as its name
//! and description, as well as the channels that it contains.

use mdflib_sys as ffi;
use std::ffi::{CStr, CString};
use std::ops::Deref;
use std::os::raw::c_char;

use crate::channel::{Channel, ChannelRef};
use crate::metadata::{MetaData, MetaDataRef};
use crate::sourceinformation::{SourceInformation, SourceInformationRef};

/// Represents an immutable reference to a channel group in an MDF file.
///
/// # Safety
/// This type holds a raw pointer to a C++ object. The pointer is only valid
/// as long as the parent MDF file/writer object exists. Do not use this
/// reference after the parent object has been dropped.
#[derive(Debug, Clone, Copy)]
pub struct ChannelGroupRef {
    pub(crate) inner: *const ffi::IChannelGroup,
}

impl std::fmt::Display for ChannelGroupRef {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "ChannelGroup {{ index: {}, name: {}, description: {}, nof_samples: {}, channel_count: {} }}",
            self.get_index(),
            self.get_name(),
            self.get_description(),
            self.get_nof_samples(),
            self.get_channel_count(),
        )
    }
}

impl ChannelGroupRef {
    pub(crate) fn new(inner: *const ffi::IChannelGroup) -> Self {
        Self { inner }
    }

    /// Gets the raw pointer to the underlying IChannelGroup.
    /// This is used for advanced operations like creating channel observers.
    pub fn as_ptr(&self) -> *const ffi::IChannelGroup {
        self.inner
    }

    /// Gets the index of the channel group.
    pub fn get_index(&self) -> u64 {
        unsafe { ffi::ChannelGroupGetIndex(self.inner) }
    }

    /// Gets the name of the channel group.
    pub fn get_name(&self) -> String {
        unsafe {
            let mut len = ffi::ChannelGroupGetName(self.inner, std::ptr::null_mut(), 0);
            if len == 0 {
                return String::new();
            }
            len += 1; // For null terminator
            let mut buf = vec![0 as c_char; len as usize];
            ffi::ChannelGroupGetName(self.inner, buf.as_mut_ptr(), len);
            CStr::from_ptr(buf.as_ptr()).to_string_lossy().into_owned()
        }
    }

    /// Gets the description of the channel group.
    pub fn get_description(&self) -> String {
        unsafe {
            let mut len = ffi::ChannelGroupGetDescription(self.inner, std::ptr::null_mut(), 0);
            if len == 0 {
                return String::new();
            }
            len += 1; // For null terminator
            let mut buf = vec![0 as c_char; len as usize];
            ffi::ChannelGroupGetDescription(self.inner, buf.as_mut_ptr(), len);
            CStr::from_ptr(buf.as_ptr()).to_string_lossy().into_owned()
        }
    }

    /// Gets the number of samples in the channel group.
    pub fn get_nof_samples(&self) -> u64 {
        unsafe { ffi::ChannelGroupGetNofSamples(self.inner) }
    }

    /// Gets the number of channels in the channel group.
    pub fn get_channel_count(&self) -> usize {
        unsafe { ffi::ChannelGroupGetChannelCount(self.inner) }
    }

    /// Gets a channel by its index.
    pub fn get_channel_by_index(&self, index: usize) -> Option<ChannelRef<'_>> {
        unsafe {
            let ch = ffi::ChannelGroupGetChannelByIndex(self.inner, index);
            if ch.is_null() {
                None
            } else {
                Some(ChannelRef::new(ch))
            }
        }
    }

    /// Gets a channel by its name.
    ///
    /// Note that the function search for a name that includes the search name.
    /// Example if the search name is '.DataLength', the signal with the name
    /// 'CAN_DataFrame.DataLength' will be returned the name instead of the
    /// full name
    pub fn get_channel(&self, name: &str) -> Option<ChannelRef<'_>> {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let ch = ffi::ChannelGroupGetChannelByName(self.inner, c_name.as_ptr());
            if ch.is_null() {
                None
            } else {
                Some(ChannelRef::new(ch))
            }
        }
    }

    pub fn get_channels(&self) -> Vec<ChannelRef<'_>> {
        let count = self.get_channel_count();
        (0..count)
            .filter_map(|i| self.get_channel_by_index(i))
            .collect()
    }

    /// Gets the metadata of the channel group.
    pub fn get_metadata(&self) -> Option<MetaDataRef<'_>> {
        unsafe {
            let metadata = ffi::ChannelGroupGetMetaData(self.inner);
            if metadata.is_null() {
                None
            } else {
                Some(MetaDataRef::new(metadata))
            }
        }
    }

    /// Gets the source information of the channel group.
    pub fn get_source_information(&self) -> Option<SourceInformationRef<'_>> {
        unsafe {
            let source_info = ffi::ChannelGroupGetSourceInformation(self.inner);
            if source_info.is_null() {
                None
            } else {
                Some(SourceInformationRef::new(source_info))
            }
        }
    }

    /// Gets the bus type of the channel group.
    pub fn get_bus_type(&self) -> u8 {
        unsafe { ffi::ChannelGroupGetBusType(self.inner) }
    }
}

/// Represents a mutable reference to a channel group in an MDF file.
#[derive(Debug)]
pub struct ChannelGroup {
    pub(crate) inner: *mut ffi::IChannelGroup,
    inner_ref: ChannelGroupRef,
}

impl ChannelGroup {
    pub(crate) fn new(inner: *mut ffi::IChannelGroup) -> Self {
        Self {
            inner,
            inner_ref: ChannelGroupRef::new(inner),
        }
    }

    /// Sets the name of the channel group.
    pub fn set_name(&mut self, name: &str) {
        let c_name = CString::new(name).unwrap();
        unsafe {
            ffi::ChannelGroupSetName(self.inner, c_name.as_ptr());
        }
    }

    /// Sets the description of the channel group.
    pub fn set_description(&mut self, description: &str) {
        let c_description = CString::new(description).unwrap();
        unsafe {
            ffi::ChannelGroupSetDescription(self.inner, c_description.as_ptr());
        }
    }

    /// Sets the number of samples in the channel group.
    pub fn set_nof_samples(&mut self, samples: u64) {
        unsafe {
            ffi::ChannelGroupSetNofSamples(self.inner, samples);
        }
    }

    /// Creates a new channel in the channel group.
    pub fn create_channel(&mut self) -> Option<Channel<'_>> {
        unsafe {
            let ch = ffi::ChannelGroupCreateChannel(self.inner);
            if ch.is_null() {
                None
            } else {
                Some(Channel::new(ch))
            }
        }
    }

    /// Creates metadata for the channel group.
    pub fn create_metadata(&mut self) -> Option<MetaData<'_>> {
        unsafe {
            let metadata = ffi::ChannelGroupCreateMetaData(self.inner);
            if metadata.is_null() {
                None
            } else {
                Some(MetaData::new(metadata))
            }
        }
    }

    /// Creates source information for the channel group.
    pub fn create_source_information(&mut self) -> Option<SourceInformation<'_>> {
        unsafe {
            let source_info = ffi::ChannelGroupCreateSourceInformation(self.inner);
            if source_info.is_null() {
                None
            } else {
                Some(SourceInformation::new(source_info))
            }
        }
    }
}

impl std::fmt::Display for ChannelGroup {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.inner_ref)
    }
}

impl Deref for ChannelGroup {
    type Target = ChannelGroupRef;

    fn deref(&self) -> &Self::Target {
        &self.inner_ref
    }
}