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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
use super::*;
use crate::core::*;
use crate::sys;
use alloc::string::String;
use alloc::string::ToString;
impl World {
pub(crate) unsafe fn to_json_id_internal<T: IntoId, const DO_CHECKS: bool>(
&self,
tid: T,
value: *const <T as IntoId>::CastType,
) -> Option<String> {
if DO_CHECKS {
assert!(!value.is_null(), "value pointer is null");
}
let tid: u64 = *tid.into_id(self);
let world = self.world_ptr();
unsafe {
let json_ptr = sys::ecs_ptr_to_json(world, tid, value as *const core::ffi::c_void);
if DO_CHECKS && json_ptr.is_null() {
return None;
}
let json = core::ffi::CStr::from_ptr(json_ptr)
.to_string_lossy()
.into_owned();
sys::ecs_os_api.free_.expect("os api is missing")(json_ptr as *mut core::ffi::c_void);
Some(json)
}
}
/// Serialize untyped value to JSON.
///
/// # Arguments
///
/// * `tid` - The type id of the value to serialize
/// * `value` - Raw pointer to the value to serialize
///
/// # Returns
///
/// * `Some(String)` - JSON representation of the value if serialization succeeds
/// * `None` - If the value pointer is null or serialization fails
///
/// # Safety
///
/// The caller must ensure that `value` points to valid data of the type specified by `tid`,
/// or is null. This function will safely handle null pointers by returning `None`.
///
/// # Example
///
/// ```ignore
/// let value = 42i32;
/// let json = world.to_json_id(type_id, &value as *const _ as *const c_void);
/// assert_eq!(json, Some("42".to_string()));
///
/// // Null pointer returns None
/// let json = world.to_json_id(type_id, core::ptr::null());
/// assert_eq!(json, None);
/// ```
pub unsafe fn to_json_id<T: IntoId>(
&self,
tid: T,
value: *const <T as IntoId>::CastType,
) -> Option<String> {
unsafe { self.to_json_id_internal::<T, true>(tid, value) }
}
/// Serialize value to JSON.
///
/// This is a type-safe wrapper around [`to_json_id`](Self::to_json_id) that accepts a reference
/// instead of a raw pointer.
///
/// # Returns
///
/// A JSON string representation of the value. Since a valid reference is provided,
/// this will always succeed and return a `String`.
pub fn to_json<'a, T: ComponentOrPairId>(&'a self, value: &'a T::CastType) -> String {
unsafe {
self.to_json_id_internal::<u64, false>(
T::get_id(self),
value as *const T::CastType as *const core::ffi::c_void,
)
.expect("to_json should not fail with a valid reference")
}
}
/// Serialize value to JSON.
///
/// This is a type-safe wrapper around [`to_json_id`](Self::to_json_id) that accepts a reference
/// instead of a raw pointer.
///
/// # Returns
///
/// A JSON string representation of the value. Since a valid reference is provided,
/// this will always succeed and return a `String`.
pub fn to_json_dyn<'a, T>(&'a self, id: FetchedId<T>, value: &'a T) -> String {
unsafe {
self.to_json_id(id.id(), value as *const T as *const core::ffi::c_void)
.expect("to_json_dyn should not fail with a valid reference")
}
}
/// Serialize world to JSON.
pub fn to_json_world(&self, desc: Option<&WorldToJsonDesc>) -> String {
let world = self.world_ptr_mut();
let desc_ptr = desc
.map(|d| d as *const WorldToJsonDesc)
.unwrap_or(core::ptr::null());
unsafe {
let json_ptr = sys::ecs_world_to_json(world, desc_ptr);
let json = core::ffi::CStr::from_ptr(json_ptr)
.to_str()
.unwrap()
.to_string();
sys::ecs_os_api.free_.expect("os api is missing")(json_ptr as *mut core::ffi::c_void);
json
}
}
#[allow(clippy::wrong_self_convention)]
pub(crate) unsafe fn from_json_id_internal<T: IntoId, const DO_CHECKS: bool>(
&self,
tid: T,
value: *mut <T as IntoId>::CastType,
json: &str,
desc: Option<&FromJsonDesc>,
) -> bool {
if DO_CHECKS {
assert!(!value.is_null(), "value pointer is null");
}
let tid: u64 = *tid.into_id(self);
let world = self.ptr_mut();
let desc_ptr = desc
.map(|d| d as *const FromJsonDesc)
.unwrap_or(core::ptr::null());
//TODO json object to prevent multiple conversions
let json = compact_str::format_compact!("{}\0", json);
unsafe {
let result = sys::ecs_ptr_from_json(
world,
tid,
value as *mut core::ffi::c_void,
json.as_ptr() as *const _,
desc_ptr,
);
if DO_CHECKS { !result.is_null() } else { true }
}
}
/// Deserialize value from JSON.
///
/// # Arguments
///
/// * `tid` - The type id of the value to deserialize
/// * `value` - Raw pointer to the memory to write to
/// * `json` - The JSON expression to parse
/// * `desc` - Optional configuration parameters for deserializer
///
/// # Returns
///
/// * `true` - If deserialization succeeds
/// * `false` - If the value pointer is null or deserialization fails
///
/// # Safety
///
/// The caller must ensure that `value` points to valid, initialized memory large enough
/// to contain a value of the type specified by `tid`, or is null. This function will
/// safely handle null pointers by returning `false`.
///
/// # Example
///
/// ```ignore
/// let mut value = 0i32;
/// let success = world.from_json_id(type_id, &mut value as *mut _ as *mut c_void, "42", None);
/// assert!(success);
/// assert_eq!(value, 42);
///
/// // Null pointer returns false
/// let success = world.from_json_id(type_id, core::ptr::null_mut(), "42", None);
/// assert!(!success);
/// ```
pub unsafe fn from_json_id<T: IntoId>(
&self,
tid: T,
value: *mut <T as IntoId>::CastType,
json: &str,
desc: Option<&FromJsonDesc>,
) -> bool {
unsafe { self.from_json_id_internal::<T, true>(tid, value, json, desc) }
}
/// Deserialize value from JSON.
///
/// This is a type-safe wrapper around [`from_json_id`](Self::from_json_id) that accepts a mutable reference
/// instead of a raw pointer.
///
/// # Panics
///
/// Panics if deserialization fails. Since a valid reference is provided, failures indicate
/// invalid JSON or type mismatches.
pub fn from_json<T: ComponentOrPairId>(
&self,
value: &mut T::CastType,
json: &str,
desc: Option<&FromJsonDesc>,
) {
unsafe {
self.from_json_id_internal::<u64, false>(
T::CastType::get_id(self),
value as *mut T::CastType as *mut core::ffi::c_void,
json,
desc,
)
};
}
/// Deserialize JSON into world.
pub fn from_json_world(&self, json: &str, desc: Option<&FromJsonDesc>) -> &Self {
let world = self.ptr_mut();
//TODO json object to prevent multiple conversions
let json = compact_str::format_compact!("{}\0", json);
let desc_ptr = desc
.map(|d| d as *const FromJsonDesc)
.unwrap_or(core::ptr::null());
unsafe {
sys::ecs_world_from_json(world, json.as_ptr() as *const _, desc_ptr);
}
self
}
/// Deserialize JSON file into world.
pub fn from_json_world_file(
&mut self,
json_file: &str,
desc: Option<&FromJsonDesc>,
) -> &mut Self {
let world = self.ptr_mut();
//TODO json object to prevent multiple conversions
let json_file = compact_str::format_compact!("{}\0", json_file);
let desc_ptr = desc
.map(|d| d as *const FromJsonDesc)
.unwrap_or(core::ptr::null());
unsafe {
sys::ecs_world_from_json_file(world, json_file.as_ptr() as *const _, desc_ptr);
}
self
}
}