essentia-core 0.1.5

Core Rust library wrapping Essentia functionality
Documentation
use cxx::UniquePtr;
use essentia_sys::ffi;
use std::marker::PhantomData;
use thiserror::Error;

use super::types::{DataType, HasDataType};

pub enum DataContainerInner<'a> {
    Owned(UniquePtr<ffi::DataContainer>),
    Borrowed(&'a ffi::DataContainer),
}

impl<'a> AsRef<ffi::DataContainer> for DataContainerInner<'a> {
    fn as_ref(&self) -> &ffi::DataContainer {
        match self {
            DataContainerInner::Owned(ptr) => ptr.as_ref().expect("UniquePtr should not be null"),
            DataContainerInner::Borrowed(reference) => reference,
        }
    }
}

pub struct DataContainer<'a, T> {
    pub(crate) inner: DataContainerInner<'a>,
    _marker: PhantomData<T>,
}

impl<'a, T> DataContainer<'a, T> {
    pub(crate) fn new_owned(inner: UniquePtr<ffi::DataContainer>) -> Self {
        Self {
            inner: DataContainerInner::Owned(inner),
            _marker: PhantomData,
        }
    }

    pub(crate) fn new_borrowed(inner: &'a ffi::DataContainer) -> Self {
        Self {
            inner: DataContainerInner::Borrowed(inner),
            _marker: PhantomData,
        }
    }

    pub fn into_any(self) -> DataContainer<'a, super::types::phantom::Any> {
        DataContainer {
            inner: self.inner,
            _marker: PhantomData,
        }
    }

    pub fn data_type(&self) -> DataType {
        self.inner.as_ref().get_data_type().into()
    }

    pub fn into_owned_ptr(self) -> UniquePtr<ffi::DataContainer> {
        match self.inner {
            DataContainerInner::Owned(ptr) => ptr,
            DataContainerInner::Borrowed(borrowed) => copy_to_owned(borrowed),
        }
    }
}

impl<'a, T: HasDataType> DataContainer<'a, T> {
    pub fn compile_time_data_type() -> DataType {
        T::data_type()
    }

    pub fn verify_type(&self) -> Result<(), TypeMismatchError> {
        let runtime_type = self.data_type();
        let compile_time_type = Self::compile_time_data_type();

        if runtime_type == compile_time_type {
            Ok(())
        } else {
            Err(TypeMismatchError {
                expected: compile_time_type,
                actual: runtime_type,
            })
        }
    }
}

#[derive(Debug, Clone, PartialEq, Error)]
#[error("Type mismatch: expected {expected}, got {actual}")]
pub struct TypeMismatchError {
    pub expected: DataType,
    pub actual: DataType,
}

fn copy_to_owned(data: &ffi::DataContainer) -> UniquePtr<ffi::DataContainer> {
    let data_type = data.get_data_type();

    match data_type {
        ffi::DataType::Bool => {
            let value = data.get_bool().unwrap();
            ffi::create_data_container_from_bool(value)
        }
        ffi::DataType::String => {
            let value = data.get_string().unwrap();
            ffi::create_data_container_from_string(&value)
        }
        ffi::DataType::Float => {
            let value = data.get_float().unwrap();
            ffi::create_data_container_from_float(value)
        }
        ffi::DataType::Int => {
            let value = data.get_int().unwrap();
            ffi::create_data_container_from_int(value)
        }
        ffi::DataType::UnsignedInt => {
            let value = data.get_unsigned_int().unwrap();
            ffi::create_data_container_from_unsigned_int(value)
        }
        ffi::DataType::Long => {
            let value = data.get_long().unwrap();
            ffi::create_data_container_from_long(value)
        }
        ffi::DataType::StereoSample => {
            let value = data.get_stereo_sample().unwrap();
            ffi::create_data_container_from_stereo_sample(value)
        }
        ffi::DataType::VectorBool => {
            let value = data.get_vector_bool().unwrap();
            ffi::create_data_container_from_vector_bool(&value)
        }
        ffi::DataType::VectorInt => {
            let value = data.get_vector_int().unwrap();
            ffi::create_data_container_from_vector_int(value)
        }
        ffi::DataType::VectorString => {
            let strings = data.get_vector_string().unwrap();
            let str_refs: Vec<&str> = strings.iter().map(|s| s.as_str()).collect();
            ffi::create_data_container_from_vector_string(&str_refs)
        }
        ffi::DataType::VectorFloat => {
            let value = data.get_vector_float().unwrap();
            ffi::create_data_container_from_vector_float(value)
        }
        ffi::DataType::VectorStereoSample => {
            let value = data.get_vector_stereo_sample().unwrap();
            ffi::create_data_container_from_vector_stereo_sample(value)
        }
        ffi::DataType::VectorVectorFloat => {
            let value = data.get_vector_vector_float().unwrap();
            ffi::create_data_container_from_vector_vector_float(value)
        }
        ffi::DataType::MatrixFloat => {
            let value = data.get_matrix_float().unwrap();
            ffi::create_data_container_from_matrix_float(value)
        }
        ffi::DataType::VectorVectorString => {
            let value = data.get_vector_vector_string().unwrap();
            ffi::create_data_container_from_vector_vector_string(value)
        }
        ffi::DataType::VectorVectorStereoSample => {
            let value = data.get_vector_vector_stereo_sample().unwrap();
            ffi::create_data_container_from_vector_vector_stereo_sample(value)
        }
        ffi::DataType::VectorMatrixFloat => {
            let value = data.get_vector_matrix_float().unwrap();
            ffi::create_data_container_from_vector_matrix_float(value)
        }
        ffi::DataType::MapVectorFloat => {
            let value = data.get_map_vector_float().unwrap();
            ffi::create_data_container_from_map_vector_float(value)
        }
        ffi::DataType::MapVectorString => {
            let value = data.get_map_vector_string().unwrap();
            ffi::create_data_container_from_map_vector_string(value)
        }
        ffi::DataType::MapVectorInt => {
            let value = data.get_map_vector_int().unwrap();
            ffi::create_data_container_from_map_vector_int(value)
        }
        ffi::DataType::MapFloat => {
            let value = data.get_map_float().unwrap();
            ffi::create_data_container_from_map_float(value)
        }
        ffi::DataType::Pool => {
            let pool_bridge_ref = data.get_pool();
            let cloned_pool = pool_bridge_ref.clone();
            ffi::create_data_container_from_pool(cloned_pool)
        }
        data_type => {
            panic!(
                "Unsupported data type: {:?}. This indicates a bug - the Rust code is out of sync with the C++ data types.",
                data_type
            )
        }
    }
}