mdflib 0.2.1

Rust bindings for mdflib
Documentation
//! Attachment wrapper for mdflib
//!
//! An attachment is a file that is embedded or referenced within an MDF file.
//! This module provides access to the attachment's metadata, such as its filename,
//! file type, and MD5 checksum.

use crate::error::Result;
use crate::metadata::{MetaData, MetaDataRef};
use mdflib_sys as ffi;
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::ops::Deref;
use std::os::raw::c_char;

/// Represents an immutable reference to an attachment in an MDF file.
#[derive(Debug, Clone, Copy)]
pub struct AttachmentRef<'a> {
    pub(crate) inner: *const ffi::IAttachment,
    _marker: PhantomData<&'a ()>,
}

impl std::fmt::Display for AttachmentRef<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Attachment {{ index: {}, creator_index: {}, embedded: {}, compressed: {}, md5: {:?}, filename: {}, file_type: {} }}",
            self.get_index(),
            self.get_creator_index(),
            self.get_embedded(),
            self.get_compressed(),
            self.get_md5(),
            self.get_filename(),
            self.get_file_type()
        )
    }
}

impl<'a> AttachmentRef<'a> {
    #[allow(dead_code)]
    pub(crate) fn new(inner: *const ffi::IAttachment) -> Self {
        Self {
            inner,
            _marker: PhantomData,
        }
    }

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

    /// Gets the creator index of the attachment.
    pub fn get_creator_index(&self) -> u16 {
        unsafe { ffi::AttachmentGetCreatorIndex(self.inner) }
    }

    /// Gets whether the attachment is embedded.
    pub fn get_embedded(&self) -> bool {
        unsafe { ffi::AttachmentGetEmbedded(self.inner) }
    }

    /// Gets whether the attachment is compressed.
    pub fn get_compressed(&self) -> bool {
        unsafe { ffi::AttachmentGetCompressed(self.inner) }
    }

    /// Gets the MD5 hash of the attachment.
    pub fn get_md5(&self) -> Option<String> {
        unsafe {
            let mut buf = vec![0 as c_char; 33]; // MD5 is 32 chars + null terminator
            if ffi::AttachmentGetMd5(self.inner, buf.as_mut_ptr(), buf.len()) {
                Some(CStr::from_ptr(buf.as_ptr()).to_string_lossy().into_owned())
            } else {
                None
            }
        }
    }

    /// Gets the filename of the attachment.
    pub fn get_filename(&self) -> String {
        unsafe {
            let mut len = ffi::AttachmentGetFileName(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::AttachmentGetFileName(self.inner, buf.as_mut_ptr(), len);
            CStr::from_ptr(buf.as_ptr()).to_string_lossy().into_owned()
        }
    }

    /// Gets the file type of the attachment.
    pub fn get_file_type(&self) -> String {
        unsafe {
            let mut len = ffi::AttachmentGetFileType(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::AttachmentGetFileType(self.inner, buf.as_mut_ptr(), len);
            CStr::from_ptr(buf.as_ptr()).to_string_lossy().into_owned()
        }
    }

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

/// Represents a mutable attachment in an MDF file.
#[derive(Debug)]
pub struct Attachment<'a> {
    pub(crate) inner: *mut ffi::IAttachment,
    inner_ref: AttachmentRef<'a>,
}

impl<'a> Attachment<'a> {
    #[allow(dead_code)]
    pub(crate) fn new(inner: *mut ffi::IAttachment) -> Self {
        Self {
            inner,
            inner_ref: AttachmentRef::new(inner),
        }
    }

    /// Sets the creator index of the attachment.
    pub fn set_creator_index(&mut self, index: u16) {
        unsafe {
            ffi::AttachmentSetCreatorIndex(self.inner, index);
        }
    }

    /// Sets whether the attachment is embedded.
    pub fn set_embedded(&mut self, embedded: bool) {
        unsafe {
            ffi::AttachmentSetEmbedded(self.inner, embedded);
        }
    }

    /// Sets whether the attachment is compressed.
    pub fn set_compressed(&mut self, compressed: bool) {
        unsafe {
            ffi::AttachmentSetCompressed(self.inner, compressed);
        }
    }

    /// Sets the filename of the attachment.
    pub fn set_filename(&mut self, name: &str) -> Result<()> {
        let c_name = CString::new(name)?;
        unsafe {
            ffi::AttachmentSetFileName(self.inner, c_name.as_ptr());
        }
        Ok(())
    }

    /// Sets the file type of the attachment.
    pub fn set_file_type(&mut self, file_type: &str) -> Result<()> {
        let c_type = CString::new(file_type)?;
        unsafe {
            ffi::AttachmentSetFileType(self.inner, c_type.as_ptr());
        }
        Ok(())
    }

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

impl<'a> Deref for Attachment<'a> {
    type Target = AttachmentRef<'a>;

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