Skip to main content

il2cpp_bridge_rs/structs/core/members/
property.rs

1//! IL2CPP Property definition
2use super::method::Method;
3use crate::structs::core::Type;
4use std::ffi::c_void;
5
6/// Property metadata synthesized from getter and setter methods.
7#[derive(Debug, Clone)]
8pub struct Property {
9    /// Name of the property (without get_/set_ prefix)
10    pub name: String,
11    /// Type of the property
12    pub type_info: Type,
13    /// Getter method (if exists)
14    pub getter: Option<Method>,
15    /// Setter method (if exists)
16    pub setter: Option<Method>,
17    /// Whether the property is static
18    pub is_static: bool,
19    /// Access modifier
20    pub access: &'static str,
21    /// Instance pointer for instance properties
22    pub instance: Option<*mut c_void>,
23}
24
25unsafe impl Send for Property {}
26unsafe impl Sync for Property {}
27
28impl std::fmt::Display for Property {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        write!(f, "{}", self.fmt_property())
31    }
32}
33
34impl Property {
35    /// Creates a new Property from getter and/or setter methods
36    pub fn from_methods(getter: Option<Method>, setter: Option<Method>) -> Option<Self> {
37        if getter.is_none() && setter.is_none() {
38            return None;
39        }
40
41        let name = if let Some(ref g) = getter {
42            g.name[4..].to_string() // Strip "get_"
43        } else if let Some(ref s) = setter {
44            s.name[4..].to_string() // Strip "set_"
45        } else {
46            return None;
47        };
48
49        let type_info = if let Some(ref g) = getter {
50            g.return_type.clone()
51        } else if let Some(ref s) = setter {
52            s.args
53                .first()
54                .map(|a| a.type_info.clone())
55                .unwrap_or_default()
56        } else {
57            Type::default()
58        };
59
60        let is_static = getter
61            .as_ref()
62            .map(|g| g.is_static)
63            .unwrap_or_else(|| setter.as_ref().map(|s| s.is_static).unwrap_or(false));
64
65        let access = getter
66            .as_ref()
67            .map(|g| g.get_attribute())
68            .unwrap_or_else(|| {
69                setter
70                    .as_ref()
71                    .map(|s| s.get_attribute())
72                    .unwrap_or("public")
73            });
74
75        Some(Property {
76            name,
77            type_info,
78            getter,
79            setter,
80            is_static,
81            access,
82            instance: None,
83        })
84    }
85
86    /// Returns a copy of this property with the instance pointer set
87    pub fn with_instance(&self, instance: *mut c_void) -> Self {
88        let mut prop = self.clone();
89        prop.instance = Some(instance);
90        prop
91    }
92
93    /// Returns true if this property has a getter
94    pub fn has_getter(&self) -> bool {
95        self.getter.is_some()
96    }
97
98    /// Returns true if this property has a setter
99    pub fn has_setter(&self) -> bool {
100        self.setter.is_some()
101    }
102
103    /// Reads the property value through its getter.
104    ///
105    /// For instance properties, the property must have a bound instance
106    /// pointer. Use [`crate::structs::Object::property`] to obtain one.
107    pub unsafe fn get_value<T: Copy>(&self) -> Result<T, String> {
108        let getter = self
109            .getter
110            .as_ref()
111            .ok_or_else(|| format!("Property '{}' does not have a getter", self.name))?;
112
113        let mut method = getter.clone();
114
115        if !self.is_static {
116            let inst = self.instance.ok_or_else(|| {
117                format!(
118                    "Property '{}' is not static but no instance was provided. Use Object::property or set the instance manually.",
119                    self.name
120                )
121            })?;
122            method.instance = Some(inst);
123        }
124
125        method.call::<T>(&[])
126    }
127
128    /// Writes a property value through its setter.
129    ///
130    /// For instance properties, the property must have a bound instance
131    /// pointer. Use [`crate::structs::Object::property`] to obtain one.
132    pub unsafe fn set_value<T>(&self, value: T) -> Result<(), String> {
133        let setter = self
134            .setter
135            .as_ref()
136            .ok_or_else(|| format!("Property '{}' does not have a setter", self.name))?;
137
138        let mut method = setter.clone();
139
140        if !self.is_static {
141            let inst = self.instance.ok_or_else(|| {
142                format!(
143                    "Property '{}' is not static but no instance was provided. Use Object::property or set the instance manually.",
144                    self.name
145                )
146            })?;
147            method.instance = Some(inst);
148        }
149
150        let value_ptr = &value as *const T as *mut c_void;
151        method.call::<()>(&[value_ptr])?;
152        Ok(())
153    }
154
155    /// Generates a string representation of the property
156    fn fmt_property(&self) -> String {
157        let static_prefix = if self.is_static { "static " } else { "" };
158
159        let accessors = match (self.has_getter(), self.has_setter()) {
160            (true, true) => "get; set;",
161            (true, false) => "get;",
162            (false, true) => "set;",
163            _ => "",
164        };
165
166        format!(
167            "{} {}{} {} {{ {} }}",
168            self.access,
169            static_prefix,
170            self.type_info.cpp_name(),
171            self.name,
172            accessors,
173        )
174    }
175}