rusty_spine/
attachment_loader.rs

1use crate::{
2    c::{
3        spAtlasAttachmentLoader_create, spAttachmentLoader, spAttachmentLoader_createAttachment,
4        spAttachmentLoader_dispose,
5    },
6    c_interface::{NewFromPtr, SyncPtr},
7    Atlas, Attachment, AttachmentType, RegionProps, Skin, SpineError,
8};
9
10/// A loader for creating custom attachments.
11///
12/// Currently only supports [`Atlas`](`crate::Atlas`) based attachments.
13#[derive(Debug)]
14pub struct AttachmentLoader {
15    c_attachment_loader: SyncPtr<spAttachmentLoader>,
16}
17
18impl NewFromPtr<spAttachmentLoader> for AttachmentLoader {
19    unsafe fn new_from_ptr(c_attachment_loader: *mut spAttachmentLoader) -> Self {
20        Self {
21            c_attachment_loader: SyncPtr(c_attachment_loader),
22        }
23    }
24}
25
26impl AttachmentLoader {
27    /// The spine runtime offers a default [`AttachmentLoader`](`crate::AttachmentLoader`) that
28    /// loads attachments from an [`Atlas`](`crate::Atlas`).
29    pub fn new_atlas_loader(atlas: &Atlas) -> Self {
30        unsafe {
31            let atlas_attachment_loader = spAtlasAttachmentLoader_create(atlas.c_ptr());
32            let attachment_loader = &mut (*atlas_attachment_loader).super_0;
33            Self::new_from_ptr(attachment_loader)
34        }
35    }
36
37    /// Creates an [`Attachment`](`crate::Attachment`) of a specified type.
38    ///
39    /// # Errors
40    ///
41    /// Returns [`SpineError::CreationFailed`] if creating the attachment failed.
42    /// Check [`error1`](`Self::error1`) and [`error2`](`Self::error2`) for more information.
43    /// Returns [`SpineError::NulError`] if `name` or `path` contain a null byte.
44    pub fn create_attachment(
45        &self,
46        skin: Option<Skin>,
47        attachment_type: AttachmentType,
48        name: &str,
49        path: &str,
50    ) -> Result<Attachment, SpineError> {
51        let c_name = std::ffi::CString::new(name)?;
52        let c_path = std::ffi::CString::new(path)?;
53
54        unsafe {
55            let c_name = c_name.as_ptr();
56            let c_path = c_path.as_ptr();
57            let c_skin = skin.map_or(std::ptr::null_mut(), |skin| skin.c_ptr());
58            let c_sequence = std::ptr::null_mut(); // What is this for?
59
60            let attachment = spAttachmentLoader_createAttachment(
61                self.c_ptr(),
62                c_skin,
63                attachment_type as u32,
64                c_name,
65                c_path,
66                c_sequence,
67            );
68
69            if attachment.is_null() {
70                Err(SpineError::new_creation_failed("Attachment"))
71            } else {
72                Ok(Attachment::new_from_ptr(attachment))
73            }
74        }
75    }
76
77    /// Convenience function for creating a [`RegionAttachment`](`crate::RegionAttachment`).
78    ///
79    /// # Errors
80    ///
81    /// Returns [`SpineError::CreationFailed`] if creating the attachment failed.
82    /// Check [`error1`](`Self::error1`) and [`error2`](`Self::error2`) for more information.
83    /// Returns [`SpineError::NulError`] if `name` or `path` contain a null byte.
84    pub fn create_region_attachment(
85        &self,
86        skin: Option<Skin>,
87        name: &str,
88        path: &str,
89        props: &RegionProps,
90    ) -> Result<Attachment, SpineError> {
91        let attachment = self.create_attachment(skin, AttachmentType::Region, name, path)?;
92
93        let Some(mut region) = attachment.as_region() else {
94            return Err(SpineError::new_creation_failed("RegionAttachment"));
95        };
96
97        region.update_from_props(props);
98
99        Ok(attachment)
100    }
101
102    c_accessor_string!(error1, error1);
103    c_accessor_string!(error2, error2);
104    c_ptr!(c_attachment_loader, spAttachmentLoader);
105}
106
107impl Drop for AttachmentLoader {
108    fn drop(&mut self) {
109        unsafe {
110            spAttachmentLoader_dispose(self.c_ptr());
111        }
112    }
113}