1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use crate::{
    bounding_box_attachment::BoundingBoxAttachment,
    c::{
        spAttachment, spAttachmentType, spAttachment_dispose, spBoundingBoxAttachment,
        spClippingAttachment, spMeshAttachment, spPointAttachment, spRegionAttachment,
    },
    c_interface::{NewFromPtr, SyncPtr},
    clipping_attachment::ClippingAttachment,
    mesh_attachment::MeshAttachment,
    point_attachment::PointAttachment,
    region_attachment::RegionAttachment,
};

/// Slot attachments.
///
/// [Spine API Reference](http://esotericsoftware.com/spine-api-reference#Attachment)
///
/// Attachments are reference counted and can be stored and worked with directly, however, some of
/// the underlying data that attachments point to may be cleared. For this reason, attachments
/// should be used with caution, and only used so long as the
/// [`SkeletonData`](`crate::SkeletonData`) they came from remains valid, and only attached to the
/// slot they are meant for.
#[derive(Debug)]
pub struct Attachment {
    c_attachment: SyncPtr<spAttachment>,
}

impl NewFromPtr<spAttachment> for Attachment {
    unsafe fn new_from_ptr(c_attachment: *mut spAttachment) -> Self {
        (*c_attachment).refCount += 1;
        Self {
            c_attachment: SyncPtr(c_attachment),
        }
    }
}

impl Attachment {
    /// Get this attachment as a [`RegionAttachment`], or [`None`] if it's a different type.
    #[must_use]
    pub fn as_region(&self) -> Option<RegionAttachment> {
        if self.attachment_type() == AttachmentType::Region {
            Some(RegionAttachment::new_from_ptr(
                self.c_attachment.0.cast::<spRegionAttachment>(),
            ))
        } else {
            None
        }
    }

    /// Get this attachment as a [`BoundingBoxAttachment`], or [`None`] if it's a different type.
    #[must_use]
    pub fn as_bounding_box(&self) -> Option<BoundingBoxAttachment> {
        if self.attachment_type() == AttachmentType::BoundingBox {
            Some(unsafe {
                BoundingBoxAttachment::new_from_ptr(
                    self.c_attachment.0.cast::<spBoundingBoxAttachment>(),
                )
            })
        } else {
            None
        }
    }

    /// Get this attachment as a [`MeshAttachment`], or [`None`] if it's a different type.
    #[must_use]
    pub fn as_mesh(&self) -> Option<MeshAttachment> {
        if self.attachment_type() == AttachmentType::Mesh {
            Some(unsafe {
                MeshAttachment::new_from_ptr(self.c_attachment.0.cast::<spMeshAttachment>())
            })
        } else {
            None
        }
    }

    /// Get this attachment as a [`PointAttachment`], or [`None`] if it's a different type.
    #[must_use]
    pub fn as_point(&self) -> Option<PointAttachment> {
        if self.attachment_type() == AttachmentType::Point {
            Some(unsafe {
                PointAttachment::new_from_ptr(self.c_attachment.0.cast::<spPointAttachment>())
            })
        } else {
            None
        }
    }

    /// Get this attachment as a [`ClippingAttachment`], or [`None`] if it's a different type.
    #[must_use]
    pub fn as_clipping(&self) -> Option<ClippingAttachment> {
        if self.attachment_type() == AttachmentType::Clipping {
            Some(unsafe {
                ClippingAttachment::new_from_ptr(self.c_attachment.0.cast::<spClippingAttachment>())
            })
        } else {
            None
        }
    }

    c_accessor_string!(
        /// The attachment's name.
        name,
        name
    );
    c_accessor_enum!(
        /// The attachment's type.
        attachment_type,
        type_0,
        AttachmentType
    );
    c_ptr!(c_attachment, spAttachment);
}

impl Clone for Attachment {
    fn clone(&self) -> Self {
        unsafe { Attachment::new_from_ptr(self.c_ptr()) }
    }
}

impl Drop for Attachment {
    fn drop(&mut self) {
        unsafe {
            spAttachment_dispose(self.c_ptr());
        }
    }
}

/// The type variants of an [`Attachment`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttachmentType {
    Region = 0,
    BoundingBox = 1,
    Mesh = 2,
    LinkedMesh = 3,
    Path = 4,
    Point = 5,
    Clipping = 6,
    Unknown = 99,
}

impl From<spAttachmentType> for AttachmentType {
    fn from(attachment_type: spAttachmentType) -> Self {
        match attachment_type {
            0 => Self::Region,
            1 => Self::BoundingBox,
            2 => Self::Mesh,
            3 => Self::LinkedMesh,
            4 => Self::Path,
            5 => Self::Point,
            6 => Self::Clipping,
            _ => Self::Unknown,
        }
    }
}