1#![allow(clippy::missing_safety_doc)]
2
3use std::ffi::CStr;
4use std::os::raw::c_char;
5use std::sync::Arc;
6
7use polyxml::schema::{
8 FieldKind, FieldSchema, ModelSchema, ModelSchemaBuilder, ScalarType, ValueType,
9};
10use polyxml::value::PolyValue;
11
12pub struct PolyXmlSchemaBuilder(ModelSchemaBuilder);
14pub struct PolyXmlSchema(Arc<ModelSchema>);
15pub struct PolyXmlValue(PolyValue);
16
17#[repr(C)]
18pub enum PolyXmlFieldKind {
19 Attribute = 0,
20 Element = 1,
21 Text = 2,
22}
23
24#[repr(C)]
25pub enum PolyXmlScalarType {
26 String = 0,
27 Int = 1,
28 Float = 2,
29 Bool = 3,
30 Decimal = 4,
31 XmlDate = 5,
32 XmlDateTime = 6,
33 Any = 7,
34}
35
36#[repr(C)]
37pub enum PolyXmlErrorCode {
38 Ok = 0,
39 ErrSyntax = 1,
40 ErrScalar = 2,
41 ErrSchema = 3,
42 ErrNullPtr = 4,
43 ErrUtf8 = 5,
44}
45
46#[no_mangle]
47pub unsafe extern "C" fn polyxml_schema_builder_create(
48 name: *const c_char,
49) -> *mut PolyXmlSchemaBuilder {
50 if name.is_null() {
51 return std::ptr::null_mut();
52 }
53 let name_str = match CStr::from_ptr(name).to_str() {
54 Ok(s) => s,
55 Err(_) => return std::ptr::null_mut(),
56 };
57 Box::into_raw(Box::new(PolyXmlSchemaBuilder(ModelSchema::builder(
58 name_str,
59 ))))
60}
61
62#[no_mangle]
63pub unsafe extern "C" fn polyxml_schema_builder_add_field(
64 builder: *mut PolyXmlSchemaBuilder,
65 name: *const c_char,
66 xml_name: *const c_char,
67 kind: PolyXmlFieldKind,
68 scalar_type: PolyXmlScalarType,
69) {
70 if builder.is_null() || name.is_null() || xml_name.is_null() {
71 return;
72 }
73 let name_str = match CStr::from_ptr(name).to_str() {
74 Ok(s) => s,
75 Err(_) => return,
76 };
77 let xml_bytes = CStr::from_ptr(xml_name).to_bytes();
78
79 let field_kind = match kind {
80 PolyXmlFieldKind::Attribute => FieldKind::Attribute,
81 PolyXmlFieldKind::Element => FieldKind::Element,
82 PolyXmlFieldKind::Text => FieldKind::Text,
83 };
84
85 let sc_type = match scalar_type {
86 PolyXmlScalarType::String => ScalarType::String,
87 PolyXmlScalarType::Int => ScalarType::Int,
88 PolyXmlScalarType::Float => ScalarType::Float,
89 PolyXmlScalarType::Bool => ScalarType::Bool,
90 PolyXmlScalarType::Decimal => ScalarType::Decimal,
91 PolyXmlScalarType::XmlDate => ScalarType::XmlDate,
92 PolyXmlScalarType::XmlDateTime => ScalarType::XmlDateTime,
93 PolyXmlScalarType::Any => ScalarType::Any,
94 };
95
96 let field = FieldSchema::new(name_str, xml_bytes, field_kind, ValueType::Scalar(sc_type));
97 let b = &mut *builder;
98 b.0 = std::mem::replace(&mut b.0, ModelSchema::builder("")).field(field);
99}
100
101#[no_mangle]
102pub unsafe extern "C" fn polyxml_schema_builder_build(
103 builder: *mut PolyXmlSchemaBuilder,
104) -> *mut PolyXmlSchema {
105 if builder.is_null() {
106 return std::ptr::null_mut();
107 }
108 let boxed = Box::from_raw(builder);
109 let schema = boxed.0.build();
110 Box::into_raw(Box::new(PolyXmlSchema(schema)))
111}
112
113#[no_mangle]
114pub unsafe extern "C" fn polyxml_schema_free(schema: *mut PolyXmlSchema) {
115 if !schema.is_null() {
116 drop(Box::from_raw(schema));
117 }
118}
119
120#[no_mangle]
121pub unsafe extern "C" fn polyxml_deserialize(
122 data: *const u8,
123 len: usize,
124 schema: *const PolyXmlSchema,
125 out_value: *mut *mut PolyXmlValue,
126) -> PolyXmlErrorCode {
127 if data.is_null() || schema.is_null() || out_value.is_null() {
128 return PolyXmlErrorCode::ErrNullPtr;
129 }
130
131 let slice = std::slice::from_raw_parts(data, len);
132 let s = &(*schema).0;
133
134 match polyxml::deserialize(slice, Arc::clone(s)) {
135 Ok(val) => {
136 *out_value = Box::into_raw(Box::new(PolyXmlValue(val)));
137 PolyXmlErrorCode::Ok
138 }
139 Err(polyxml::error::PolyXmlError::XmlSyntaxError { .. }) => PolyXmlErrorCode::ErrSyntax,
140 Err(polyxml::error::PolyXmlError::ScalarParseError { .. }) => PolyXmlErrorCode::ErrScalar,
141 Err(_) => PolyXmlErrorCode::ErrSchema,
142 }
143}
144
145#[no_mangle]
146pub unsafe extern "C" fn polyxml_serialize(
147 root_name: *const c_char,
148 value: *const PolyXmlValue,
149 schema: *const PolyXmlSchema,
150 indent: i32,
151 out_bytes: *mut *mut u8,
152 out_len: *mut usize,
153) -> PolyXmlErrorCode {
154 if root_name.is_null()
155 || value.is_null()
156 || schema.is_null()
157 || out_bytes.is_null()
158 || out_len.is_null()
159 {
160 return PolyXmlErrorCode::ErrNullPtr;
161 }
162
163 let root_str = match CStr::from_ptr(root_name).to_str() {
164 Ok(s) => s,
165 Err(_) => return PolyXmlErrorCode::ErrUtf8,
166 };
167
168 let indent_opt = if indent > 0 {
169 Some(indent as usize)
170 } else {
171 None
172 };
173 let val = &(*value).0;
174 let s = &(*schema).0;
175
176 match polyxml::serialize(root_str, val, s, indent_opt) {
177 Ok(mut bytes) => {
178 bytes.shrink_to_fit();
179 *out_len = bytes.len();
180 *out_bytes = bytes.as_mut_ptr();
181 std::mem::forget(bytes);
182 PolyXmlErrorCode::Ok
183 }
184 Err(_) => PolyXmlErrorCode::ErrSchema,
185 }
186}
187
188#[no_mangle]
189pub unsafe extern "C" fn polyxml_bytes_free(bytes: *mut u8, len: usize) {
190 if !bytes.is_null() {
191 drop(Vec::from_raw_parts(bytes, len, len));
192 }
193}
194
195#[no_mangle]
196pub unsafe extern "C" fn polyxml_value_get_field(
197 val: *const PolyXmlValue,
198 key: *const c_char,
199) -> *const PolyXmlValue {
200 if val.is_null() || key.is_null() {
201 return std::ptr::null();
202 }
203 let key_str = match CStr::from_ptr(key).to_str() {
204 Ok(s) => s,
205 Err(_) => return std::ptr::null(),
206 };
207 match &(*val).0 {
208 PolyValue::Object(map) => {
209 if let Some(sub_val) = map.get(key_str) {
210 sub_val as *const PolyValue as *const PolyXmlValue
212 } else {
213 std::ptr::null()
214 }
215 }
216 _ => std::ptr::null(),
217 }
218}
219
220#[no_mangle]
221pub unsafe extern "C" fn polyxml_value_get_int(
222 val: *const PolyXmlValue,
223 out_int: *mut i64,
224) -> PolyXmlErrorCode {
225 if val.is_null() || out_int.is_null() {
226 return PolyXmlErrorCode::ErrNullPtr;
227 }
228 if let Some(i) = (*val).0.as_i64() {
229 *out_int = i;
230 PolyXmlErrorCode::Ok
231 } else {
232 PolyXmlErrorCode::ErrScalar
233 }
234}
235
236#[no_mangle]
237pub unsafe extern "C" fn polyxml_value_get_float(
238 val: *const PolyXmlValue,
239 out_float: *mut f64,
240) -> PolyXmlErrorCode {
241 if val.is_null() || out_float.is_null() {
242 return PolyXmlErrorCode::ErrNullPtr;
243 }
244 if let Some(f) = (*val).0.as_f64() {
245 *out_float = f;
246 PolyXmlErrorCode::Ok
247 } else {
248 PolyXmlErrorCode::ErrScalar
249 }
250}
251
252#[no_mangle]
253pub unsafe extern "C" fn polyxml_value_get_bool(
254 val: *const PolyXmlValue,
255 out_bool: *mut bool,
256) -> PolyXmlErrorCode {
257 if val.is_null() || out_bool.is_null() {
258 return PolyXmlErrorCode::ErrNullPtr;
259 }
260 if let Some(b) = (*val).0.as_bool() {
261 *out_bool = b;
262 PolyXmlErrorCode::Ok
263 } else {
264 PolyXmlErrorCode::ErrScalar
265 }
266}
267
268#[no_mangle]
269pub unsafe extern "C" fn polyxml_value_get_string(
270 val: *const PolyXmlValue,
271 out_str: *mut *const c_char,
272 out_len: *mut usize,
273) -> PolyXmlErrorCode {
274 if val.is_null() || out_str.is_null() || out_len.is_null() {
275 return PolyXmlErrorCode::ErrNullPtr;
276 }
277 if let Some(s) = (*val).0.as_str() {
278 *out_str = s.as_ptr() as *const c_char;
279 *out_len = s.len();
280 PolyXmlErrorCode::Ok
281 } else {
282 PolyXmlErrorCode::ErrScalar
283 }
284}
285
286#[no_mangle]
287pub unsafe extern "C" fn polyxml_value_get_list_len(
288 val: *const PolyXmlValue,
289 out_len: *mut usize,
290) -> PolyXmlErrorCode {
291 if val.is_null() || out_len.is_null() {
292 return PolyXmlErrorCode::ErrNullPtr;
293 }
294 if let Some(l) = (*val).0.as_list() {
295 *out_len = l.len();
296 PolyXmlErrorCode::Ok
297 } else {
298 PolyXmlErrorCode::ErrScalar
299 }
300}
301
302#[no_mangle]
303pub unsafe extern "C" fn polyxml_value_get_list_item(
304 val: *const PolyXmlValue,
305 idx: usize,
306) -> *const PolyXmlValue {
307 if val.is_null() {
308 return std::ptr::null();
309 }
310 if let Some(l) = (*val).0.as_list() {
311 if let Some(item) = l.get(idx) {
312 return item as *const PolyValue as *const PolyXmlValue;
313 }
314 }
315 std::ptr::null()
316}
317
318#[no_mangle]
319pub unsafe extern "C" fn polyxml_value_is_null(val: *const PolyXmlValue) -> bool {
320 if val.is_null() {
321 return true;
322 }
323 (*val).0.is_null()
324}
325
326#[no_mangle]
327pub unsafe extern "C" fn polyxml_value_free(val: *mut PolyXmlValue) {
328 if !val.is_null() {
329 drop(Box::from_raw(val));
330 }
331}
332
333#[no_mangle]
334pub extern "C" fn polyxml_version() -> *const c_char {
335 static VERSION: &[u8] = concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes();
336 VERSION.as_ptr() as *const c_char
337}