1use chemfiles_sys as ffi;
4
5use crate::errors::{check, check_not_null, check_success, Error};
6use crate::strings;
7
8#[derive(Debug)]
10pub(crate) struct RawProperty {
11 handle: *mut ffi::CHFL_PROPERTY,
12}
13
14impl RawProperty {
15 pub unsafe fn from_ptr(ptr: *mut ffi::CHFL_PROPERTY) -> RawProperty {
19 check_not_null(ptr);
20 RawProperty { handle: ptr }
21 }
22
23 pub fn as_ptr(&self) -> *const ffi::CHFL_PROPERTY {
25 self.handle
26 }
27
28 fn double(value: f64) -> RawProperty {
29 unsafe {
30 let handle = ffi::chfl_property_double(value);
31 RawProperty::from_ptr(handle)
32 }
33 }
34
35 fn bool(value: bool) -> RawProperty {
36 unsafe {
37 let handle = ffi::chfl_property_bool(u8::from(value));
38 RawProperty::from_ptr(handle)
39 }
40 }
41
42 fn vector3d(value: [f64; 3]) -> RawProperty {
43 unsafe {
44 let handle = ffi::chfl_property_vector3d(value.as_ptr());
45 RawProperty::from_ptr(handle)
46 }
47 }
48
49 fn string(value: &str) -> RawProperty {
50 let buffer = strings::to_c(value);
51 unsafe {
52 let handle = ffi::chfl_property_string(buffer.as_ptr());
53 RawProperty::from_ptr(handle)
54 }
55 }
56
57 fn get_kind(&self) -> ffi::chfl_property_kind {
58 let mut kind = ffi::chfl_property_kind::CHFL_PROPERTY_BOOL;
59 unsafe {
60 check_success(ffi::chfl_property_get_kind(self.as_ptr(), &mut kind));
61 }
62 return kind;
63 }
64
65 fn get_bool(&self) -> Result<bool, Error> {
66 let mut value = 0;
67 unsafe {
68 check(ffi::chfl_property_get_bool(self.as_ptr(), &mut value))?;
69 }
70 return Ok(value != 0);
71 }
72
73 fn get_double(&self) -> Result<f64, Error> {
74 let mut value = 0.0;
75 unsafe {
76 check(ffi::chfl_property_get_double(self.as_ptr(), &mut value))?;
77 }
78 return Ok(value);
79 }
80
81 fn get_string(&self) -> Result<String, Error> {
82 let get_string = |ptr, len| unsafe { ffi::chfl_property_get_string(self.as_ptr(), ptr, len) };
83 let value = strings::call_autogrow_buffer(64, get_string)?;
84 return Ok(strings::from_c(value.as_ptr()));
85 }
86
87 fn get_vector3d(&self) -> Result<[f64; 3], Error> {
88 let mut value = [0.0; 3];
89 unsafe {
90 check(ffi::chfl_property_get_vector3d(self.as_ptr(), value.as_mut_ptr()))?;
91 }
92 return Ok(value);
93 }
94}
95
96impl Drop for RawProperty {
97 fn drop(&mut self) {
98 unsafe {
99 let _ = ffi::chfl_free(self.as_ptr().cast());
100 }
101 }
102}
103
104#[derive(Debug, Clone, PartialEq, PartialOrd)]
107pub enum Property {
108 Bool(bool),
110 Double(f64),
112 String(String),
114 Vector3D([f64; 3]),
116}
117
118impl From<bool> for Property {
119 fn from(value: bool) -> Self {
120 Property::Bool(value)
121 }
122}
123
124impl From<f64> for Property {
125 fn from(value: f64) -> Self {
126 Property::Double(value)
127 }
128}
129
130impl From<String> for Property {
131 fn from(value: String) -> Self {
132 Property::String(value)
133 }
134}
135
136impl<'a> From<&'a str> for Property {
137 fn from(value: &'a str) -> Self {
138 Property::String(value.into())
139 }
140}
141
142impl From<[f64; 3]> for Property {
143 fn from(value: [f64; 3]) -> Self {
144 Property::Vector3D(value)
145 }
146}
147
148impl Property {
149 pub(crate) fn as_raw(&self) -> RawProperty {
150 match *self {
151 Property::Bool(value) => RawProperty::bool(value),
152 Property::Double(value) => RawProperty::double(value),
153 Property::String(ref value) => RawProperty::string(value),
154 Property::Vector3D(value) => RawProperty::vector3d(value),
155 }
156 }
157
158 #[allow(clippy::needless_pass_by_value)] pub(crate) fn from_raw(raw: RawProperty) -> Property {
160 match raw.get_kind() {
161 ffi::chfl_property_kind::CHFL_PROPERTY_BOOL => Self::Bool(raw.get_bool().expect("should be a bool")),
162 ffi::chfl_property_kind::CHFL_PROPERTY_DOUBLE => {
163 Self::Double(raw.get_double().expect("should be a double"))
164 }
165 ffi::chfl_property_kind::CHFL_PROPERTY_STRING => {
166 Self::String(raw.get_string().expect("should be a string"))
167 }
168 ffi::chfl_property_kind::CHFL_PROPERTY_VECTOR3D => {
169 Property::Vector3D(raw.get_vector3d().expect("should be a vector3d"))
170 }
171 }
172 }
173}
174
175pub struct PropertiesIter<'a> {
177 pub(crate) names: std::vec::IntoIter<String>,
178 pub(crate) getter: Box<dyn Fn(&str) -> Property + 'a>,
179}
180
181impl<'a> Iterator for PropertiesIter<'a> {
182 type Item = (String, Property);
183 fn next(&mut self) -> Option<Self::Item> {
184 self.names.next().map(|name| {
185 let property = (self.getter)(&name);
186 (name, property)
187 })
188 }
189
190 fn size_hint(&self) -> (usize, Option<usize>) {
191 self.names.size_hint()
192 }
193
194 fn count(self) -> usize {
195 self.names.count()
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 mod raw {
202 use super::super::*;
203
204 #[test]
205 fn bool() {
206 let property = RawProperty::bool(false);
207 assert_eq!(property.get_kind(), ffi::chfl_property_kind::CHFL_PROPERTY_BOOL);
208 assert_eq!(property.get_bool(), Ok(false));
209 }
210
211 #[test]
212 fn double() {
213 let property = RawProperty::double(45.0);
214 assert_eq!(property.get_kind(), ffi::chfl_property_kind::CHFL_PROPERTY_DOUBLE);
215 assert_eq!(property.get_double(), Ok(45.0));
216 }
217
218 #[test]
219 fn string() {
220 let property = RawProperty::string("test");
221 assert_eq!(property.get_kind(), ffi::chfl_property_kind::CHFL_PROPERTY_STRING);
222 assert_eq!(property.get_string(), Ok("test".into()));
223 }
224
225 #[test]
226 fn vector3d() {
227 let property = RawProperty::vector3d([1.2, 3.4, 5.6]);
228 assert_eq!(property.get_kind(), ffi::chfl_property_kind::CHFL_PROPERTY_VECTOR3D);
229 assert_eq!(property.get_vector3d(), Ok([1.2, 3.4, 5.6]));
230 }
231 }
232
233 mod rust {
234 use super::super::*;
235
236 #[test]
237 fn bool() {
238 let property = Property::Bool(false);
239
240 let raw = property.as_raw();
241 assert_eq!(raw.get_kind(), ffi::chfl_property_kind::CHFL_PROPERTY_BOOL);
242 assert_eq!(raw.get_bool(), Ok(false));
243
244 assert_eq!(Property::from_raw(raw), property);
245 }
246
247 #[test]
248 fn double() {
249 let property = Property::Double(45.0);
250
251 let raw = property.as_raw();
252 assert_eq!(raw.get_kind(), ffi::chfl_property_kind::CHFL_PROPERTY_DOUBLE);
253 assert_eq!(raw.get_double(), Ok(45.0));
254
255 assert_eq!(Property::from_raw(raw), property);
256 }
257
258 #[test]
259 fn string() {
260 let property = Property::String("test".into());
261
262 let raw = property.as_raw();
263 assert_eq!(raw.get_kind(), ffi::chfl_property_kind::CHFL_PROPERTY_STRING);
264 assert_eq!(raw.get_string(), Ok("test".into()));
265
266 assert_eq!(Property::from_raw(raw), property);
267
268 let property = Property::String("long string ".repeat(128));
269 let raw = property.as_raw();
270 assert_eq!(raw.get_string(), Ok("long string ".repeat(128)));
271 }
272
273 #[test]
274 fn vector3d() {
275 let property = Property::Vector3D([1.2, 3.4, 5.6]);
276
277 let raw = property.as_raw();
278 assert_eq!(raw.get_kind(), ffi::chfl_property_kind::CHFL_PROPERTY_VECTOR3D);
279 assert_eq!(raw.get_vector3d(), Ok([1.2, 3.4, 5.6]));
280
281 assert_eq!(Property::from_raw(raw), property);
282 }
283 }
284}