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_set_namespace(
64 builder: *mut PolyXmlSchemaBuilder,
65 namespace_uri: *const c_char,
66) {
67 if builder.is_null() || namespace_uri.is_null() {
68 return;
69 }
70 let ns_str = match CStr::from_ptr(namespace_uri).to_str() {
71 Ok(s) => s,
72 Err(_) => return,
73 };
74 let b = &mut *builder;
75 b.0 = std::mem::replace(&mut b.0, ModelSchema::builder("")).with_namespace(ns_str);
76}
77
78#[no_mangle]
79pub unsafe extern "C" fn polyxml_schema_builder_add_field(
80 builder: *mut PolyXmlSchemaBuilder,
81 name: *const c_char,
82 xml_name: *const c_char,
83 kind: PolyXmlFieldKind,
84 scalar_type: PolyXmlScalarType,
85) {
86 polyxml_schema_builder_add_field_with_namespace(
87 builder,
88 name,
89 xml_name,
90 kind,
91 scalar_type,
92 std::ptr::null(),
93 );
94}
95
96#[no_mangle]
97pub unsafe extern "C" fn polyxml_schema_builder_add_field_with_namespace(
98 builder: *mut PolyXmlSchemaBuilder,
99 name: *const c_char,
100 xml_name: *const c_char,
101 kind: PolyXmlFieldKind,
102 scalar_type: PolyXmlScalarType,
103 namespace_uri: *const c_char,
104) {
105 if builder.is_null() || name.is_null() || xml_name.is_null() {
106 return;
107 }
108 let name_str = match CStr::from_ptr(name).to_str() {
109 Ok(s) => s,
110 Err(_) => return,
111 };
112 let xml_bytes = CStr::from_ptr(xml_name).to_bytes();
113
114 let field_kind = match kind {
115 PolyXmlFieldKind::Attribute => FieldKind::Attribute,
116 PolyXmlFieldKind::Element => FieldKind::Element,
117 PolyXmlFieldKind::Text => FieldKind::Text,
118 };
119
120 let sc_type = match scalar_type {
121 PolyXmlScalarType::String => ScalarType::String,
122 PolyXmlScalarType::Int => ScalarType::Int,
123 PolyXmlScalarType::Float => ScalarType::Float,
124 PolyXmlScalarType::Bool => ScalarType::Bool,
125 PolyXmlScalarType::Decimal => ScalarType::Decimal,
126 PolyXmlScalarType::XmlDate => ScalarType::XmlDate,
127 PolyXmlScalarType::XmlDateTime => ScalarType::XmlDateTime,
128 PolyXmlScalarType::Any => ScalarType::Any,
129 };
130
131 let mut field = FieldSchema::new(name_str, xml_bytes, field_kind, ValueType::Scalar(sc_type));
132 if !namespace_uri.is_null() {
133 if let Ok(ns_str) = CStr::from_ptr(namespace_uri).to_str() {
134 field = field.with_namespace(ns_str);
135 }
136 }
137 let b = &mut *builder;
138 b.0 = std::mem::replace(&mut b.0, ModelSchema::builder("")).field(field);
139}
140
141#[no_mangle]
142pub unsafe extern "C" fn polyxml_schema_builder_add_nested_field(
143 builder: *mut PolyXmlSchemaBuilder,
144 name: *const c_char,
145 xml_name: *const c_char,
146 nested_schema: *const PolyXmlSchema,
147 namespace_uri: *const c_char,
148) {
149 if builder.is_null() || name.is_null() || xml_name.is_null() || nested_schema.is_null() {
150 return;
151 }
152 let name_str = match CStr::from_ptr(name).to_str() {
153 Ok(s) => s,
154 Err(_) => return,
155 };
156 let xml_bytes = CStr::from_ptr(xml_name).to_bytes();
157 let nested = Arc::clone(&(*nested_schema).0);
158
159 let mut field = FieldSchema::new(
160 name_str,
161 xml_bytes,
162 FieldKind::Element,
163 ValueType::Nested(nested),
164 );
165 if !namespace_uri.is_null() {
166 if let Ok(ns_str) = CStr::from_ptr(namespace_uri).to_str() {
167 field = field.with_namespace(ns_str);
168 }
169 }
170 let b = &mut *builder;
171 b.0 = std::mem::replace(&mut b.0, ModelSchema::builder("")).field(field);
172}
173
174#[no_mangle]
175pub unsafe extern "C" fn polyxml_schema_builder_add_list_nested_field(
176 builder: *mut PolyXmlSchemaBuilder,
177 name: *const c_char,
178 xml_name: *const c_char,
179 item_schema: *const PolyXmlSchema,
180 namespace_uri: *const c_char,
181) {
182 if builder.is_null() || name.is_null() || xml_name.is_null() || item_schema.is_null() {
183 return;
184 }
185 let name_str = match CStr::from_ptr(name).to_str() {
186 Ok(s) => s,
187 Err(_) => return,
188 };
189 let xml_bytes = CStr::from_ptr(xml_name).to_bytes();
190 let item = Arc::clone(&(*item_schema).0);
191
192 let mut field = FieldSchema::new(
193 name_str,
194 xml_bytes,
195 FieldKind::Element,
196 ValueType::List(Box::new(ValueType::Nested(item))),
197 );
198 if !namespace_uri.is_null() {
199 if let Ok(ns_str) = CStr::from_ptr(namespace_uri).to_str() {
200 field = field.with_namespace(ns_str);
201 }
202 }
203 let b = &mut *builder;
204 b.0 = std::mem::replace(&mut b.0, ModelSchema::builder("")).field(field);
205}
206
207#[no_mangle]
208pub unsafe extern "C" fn polyxml_schema_builder_add_list_scalar_field(
209 builder: *mut PolyXmlSchemaBuilder,
210 name: *const c_char,
211 xml_name: *const c_char,
212 scalar_type: PolyXmlScalarType,
213 namespace_uri: *const c_char,
214) {
215 if builder.is_null() || name.is_null() || xml_name.is_null() {
216 return;
217 }
218 let name_str = match CStr::from_ptr(name).to_str() {
219 Ok(s) => s,
220 Err(_) => return,
221 };
222 let xml_bytes = CStr::from_ptr(xml_name).to_bytes();
223 let sc_type = match scalar_type {
224 PolyXmlScalarType::String => ScalarType::String,
225 PolyXmlScalarType::Int => ScalarType::Int,
226 PolyXmlScalarType::Float => ScalarType::Float,
227 PolyXmlScalarType::Bool => ScalarType::Bool,
228 PolyXmlScalarType::Decimal => ScalarType::Decimal,
229 PolyXmlScalarType::XmlDate => ScalarType::XmlDate,
230 PolyXmlScalarType::XmlDateTime => ScalarType::XmlDateTime,
231 PolyXmlScalarType::Any => ScalarType::Any,
232 };
233
234 let mut field = FieldSchema::new(
235 name_str,
236 xml_bytes,
237 FieldKind::Element,
238 ValueType::List(Box::new(ValueType::Scalar(sc_type))),
239 );
240 if !namespace_uri.is_null() {
241 if let Ok(ns_str) = CStr::from_ptr(namespace_uri).to_str() {
242 field = field.with_namespace(ns_str);
243 }
244 }
245 let b = &mut *builder;
246 b.0 = std::mem::replace(&mut b.0, ModelSchema::builder("")).field(field);
247}
248
249#[no_mangle]
250pub unsafe extern "C" fn polyxml_schema_builder_build(
251 builder: *mut PolyXmlSchemaBuilder,
252) -> *mut PolyXmlSchema {
253 if builder.is_null() {
254 return std::ptr::null_mut();
255 }
256 let boxed = Box::from_raw(builder);
257 let schema = boxed.0.build();
258 Box::into_raw(Box::new(PolyXmlSchema(schema)))
259}
260
261#[no_mangle]
262pub unsafe extern "C" fn polyxml_schema_free(schema: *mut PolyXmlSchema) {
263 if !schema.is_null() {
264 drop(Box::from_raw(schema));
265 }
266}
267
268#[no_mangle]
269pub unsafe extern "C" fn polyxml_deserialize(
270 data: *const u8,
271 len: usize,
272 schema: *const PolyXmlSchema,
273 out_value: *mut *mut PolyXmlValue,
274) -> PolyXmlErrorCode {
275 if data.is_null() || schema.is_null() || out_value.is_null() {
276 return PolyXmlErrorCode::ErrNullPtr;
277 }
278
279 let slice = std::slice::from_raw_parts(data, len);
280 let s = &(*schema).0;
281
282 match polyxml::deserialize(slice, Arc::clone(s)) {
283 Ok(val) => {
284 *out_value = Box::into_raw(Box::new(PolyXmlValue(val)));
285 PolyXmlErrorCode::Ok
286 }
287 Err(polyxml::error::PolyXmlError::XmlSyntaxError { .. }) => PolyXmlErrorCode::ErrSyntax,
288 Err(polyxml::error::PolyXmlError::ScalarParseError { .. }) => PolyXmlErrorCode::ErrScalar,
289 Err(_) => PolyXmlErrorCode::ErrSchema,
290 }
291}
292
293#[no_mangle]
294pub unsafe extern "C" fn polyxml_serialize(
295 root_name: *const c_char,
296 value: *const PolyXmlValue,
297 schema: *const PolyXmlSchema,
298 indent: i32,
299 out_bytes: *mut *mut u8,
300 out_len: *mut usize,
301) -> PolyXmlErrorCode {
302 polyxml_serialize_with_options(
303 root_name,
304 value,
305 schema,
306 indent,
307 -1,
308 std::ptr::null(),
309 std::ptr::null(),
310 0,
311 out_bytes,
312 out_len,
313 )
314}
315
316#[no_mangle]
317pub unsafe extern "C" fn polyxml_serialize_with_options(
318 root_name: *const c_char,
319 value: *const PolyXmlValue,
320 schema: *const PolyXmlSchema,
321 indent: i32,
322 enable_namespaces: i32,
323 ns_prefixes: *const *const c_char,
324 ns_uris: *const *const c_char,
325 ns_count: usize,
326 out_bytes: *mut *mut u8,
327 out_len: *mut usize,
328) -> PolyXmlErrorCode {
329 if root_name.is_null()
330 || value.is_null()
331 || schema.is_null()
332 || out_bytes.is_null()
333 || out_len.is_null()
334 {
335 return PolyXmlErrorCode::ErrNullPtr;
336 }
337
338 let root_str = match CStr::from_ptr(root_name).to_str() {
339 Ok(s) => s,
340 Err(_) => return PolyXmlErrorCode::ErrUtf8,
341 };
342
343 let indent_opt = if indent > 0 {
344 Some(indent as usize)
345 } else {
346 None
347 };
348
349 let enable_ns_opt = match enable_namespaces {
350 0 => Some(false),
351 1 => Some(true),
352 _ => None,
353 };
354
355 let ns_map = if ns_count > 0 && !ns_prefixes.is_null() && !ns_uris.is_null() {
356 let mut map = std::collections::HashMap::new();
357 let prefix_slice = std::slice::from_raw_parts(ns_prefixes, ns_count);
358 let uri_slice = std::slice::from_raw_parts(ns_uris, ns_count);
359 for i in 0..ns_count {
360 let prefix_ptr = prefix_slice[i];
361 let uri_ptr = uri_slice[i];
362 if !uri_ptr.is_null() {
363 if let Ok(uri) = CStr::from_ptr(uri_ptr).to_str() {
364 let prefix = if prefix_ptr.is_null() {
365 String::new()
366 } else if let Ok(p) = CStr::from_ptr(prefix_ptr).to_str() {
367 p.to_string()
368 } else {
369 String::new()
370 };
371 map.insert(prefix, uri.to_string());
372 }
373 }
374 }
375 Some(map)
376 } else {
377 None
378 };
379
380 let val = &(*value).0;
381 let s = &(*schema).0;
382
383 match polyxml::serialize_with_options(
384 root_str,
385 val,
386 s,
387 indent_opt,
388 enable_ns_opt,
389 ns_map.as_ref(),
390 ) {
391 Ok(mut bytes) => {
392 bytes.shrink_to_fit();
393 *out_len = bytes.len();
394 *out_bytes = bytes.as_mut_ptr();
395 std::mem::forget(bytes);
396 PolyXmlErrorCode::Ok
397 }
398 Err(_) => PolyXmlErrorCode::ErrSchema,
399 }
400}
401
402#[no_mangle]
403pub unsafe extern "C" fn polyxml_bytes_free(bytes: *mut u8, len: usize) {
404 if !bytes.is_null() {
405 drop(Vec::from_raw_parts(bytes, len, len));
406 }
407}
408
409#[no_mangle]
410pub unsafe extern "C" fn polyxml_value_create_record(
411 schema: *const PolyXmlSchema,
412) -> *mut PolyXmlValue {
413 if schema.is_null() {
414 return std::ptr::null_mut();
415 }
416 let s = Arc::clone(&(*schema).0);
417 let count = s.fields.len();
418 let val = PolyValue::Record {
419 schema: s,
420 values: vec![None; count].into_boxed_slice(),
421 };
422 Box::into_raw(Box::new(PolyXmlValue(val)))
423}
424
425#[no_mangle]
426pub unsafe extern "C" fn polyxml_value_create_list() -> *mut PolyXmlValue {
427 Box::into_raw(Box::new(PolyXmlValue(PolyValue::List(Vec::new()))))
428}
429
430#[no_mangle]
431pub unsafe extern "C" fn polyxml_value_create_list_with_capacity(
432 capacity: usize,
433) -> *mut PolyXmlValue {
434 Box::into_raw(Box::new(PolyXmlValue(PolyValue::List(Vec::with_capacity(
435 capacity,
436 )))))
437}
438
439#[no_mangle]
440pub unsafe extern "C" fn polyxml_value_create_string(
441 str_ptr: *const c_char,
442 len: usize,
443) -> *mut PolyXmlValue {
444 if str_ptr.is_null() {
445 return std::ptr::null_mut();
446 }
447 let slice = std::slice::from_raw_parts(str_ptr as *const u8, len);
448 let s = match std::str::from_utf8(slice) {
449 Ok(valid) => valid.to_string(),
450 Err(_) => return std::ptr::null_mut(),
451 };
452 Box::into_raw(Box::new(PolyXmlValue(PolyValue::String(s))))
453}
454
455#[no_mangle]
456pub unsafe extern "C" fn polyxml_value_create_int(val: i64) -> *mut PolyXmlValue {
457 Box::into_raw(Box::new(PolyXmlValue(PolyValue::Int(val))))
458}
459
460#[no_mangle]
461pub unsafe extern "C" fn polyxml_value_create_float(val: f64) -> *mut PolyXmlValue {
462 Box::into_raw(Box::new(PolyXmlValue(PolyValue::Float(val))))
463}
464
465#[no_mangle]
466pub unsafe extern "C" fn polyxml_value_create_bool(val: bool) -> *mut PolyXmlValue {
467 Box::into_raw(Box::new(PolyXmlValue(PolyValue::Bool(val))))
468}
469
470#[no_mangle]
471pub unsafe extern "C" fn polyxml_value_create_null() -> *mut PolyXmlValue {
472 Box::into_raw(Box::new(PolyXmlValue(PolyValue::Null)))
473}
474
475#[no_mangle]
476pub unsafe extern "C" fn polyxml_value_clone(val: *const PolyXmlValue) -> *mut PolyXmlValue {
477 if val.is_null() {
478 return std::ptr::null_mut();
479 }
480 Box::into_raw(Box::new(PolyXmlValue((*val).0.clone())))
481}
482
483#[no_mangle]
484pub unsafe extern "C" fn polyxml_value_set_field(
485 record: *mut PolyXmlValue,
486 key: *const c_char,
487 child_val: *mut PolyXmlValue,
488) -> PolyXmlErrorCode {
489 if record.is_null() || key.is_null() || child_val.is_null() {
490 return PolyXmlErrorCode::ErrNullPtr;
491 }
492 let key_str = match CStr::from_ptr(key).to_str() {
493 Ok(s) => s,
494 Err(_) => return PolyXmlErrorCode::ErrUtf8,
495 };
496 let child_boxed = Box::from_raw(child_val);
497 let child_inner = child_boxed.0;
498
499 match &mut (*record).0 {
500 PolyValue::Record { schema, values } => {
501 if let Some(idx) = schema.fields.iter().position(|f| f.name == key_str) {
502 values[idx] = Some(child_inner);
503 PolyXmlErrorCode::Ok
504 } else {
505 PolyXmlErrorCode::ErrSchema
506 }
507 }
508 PolyValue::Object(map) => {
509 map.insert(key_str.to_string(), child_inner);
510 PolyXmlErrorCode::Ok
511 }
512 _ => PolyXmlErrorCode::ErrSchema,
513 }
514}
515
516#[no_mangle]
517pub unsafe extern "C" fn polyxml_value_list_append(
518 list: *mut PolyXmlValue,
519 item: *mut PolyXmlValue,
520) -> PolyXmlErrorCode {
521 if list.is_null() || item.is_null() {
522 return PolyXmlErrorCode::ErrNullPtr;
523 }
524 let item_boxed = Box::from_raw(item);
525 let item_inner = item_boxed.0;
526
527 match &mut (*list).0 {
528 PolyValue::List(vec) => {
529 vec.push(item_inner);
530 PolyXmlErrorCode::Ok
531 }
532 _ => PolyXmlErrorCode::ErrSchema,
533 }
534}
535
536#[no_mangle]
537pub unsafe extern "C" fn polyxml_value_get_field(
538 val: *const PolyXmlValue,
539 key: *const c_char,
540) -> *const PolyXmlValue {
541 if val.is_null() || key.is_null() {
542 return std::ptr::null();
543 }
544 let key_str = match CStr::from_ptr(key).to_str() {
545 Ok(s) => s,
546 Err(_) => return std::ptr::null(),
547 };
548 if let Some(sub_val) = (*val).0.get(key_str) {
549 sub_val as *const PolyValue as *const PolyXmlValue
550 } else {
551 std::ptr::null()
552 }
553}
554
555#[no_mangle]
556pub unsafe extern "C" fn polyxml_value_get_int(
557 val: *const PolyXmlValue,
558 out_int: *mut i64,
559) -> PolyXmlErrorCode {
560 if val.is_null() || out_int.is_null() {
561 return PolyXmlErrorCode::ErrNullPtr;
562 }
563 if let Some(i) = (*val).0.as_i64() {
564 *out_int = i;
565 PolyXmlErrorCode::Ok
566 } else {
567 PolyXmlErrorCode::ErrScalar
568 }
569}
570
571#[no_mangle]
572pub unsafe extern "C" fn polyxml_value_get_float(
573 val: *const PolyXmlValue,
574 out_float: *mut f64,
575) -> PolyXmlErrorCode {
576 if val.is_null() || out_float.is_null() {
577 return PolyXmlErrorCode::ErrNullPtr;
578 }
579 if let Some(f) = (*val).0.as_f64() {
580 *out_float = f;
581 PolyXmlErrorCode::Ok
582 } else {
583 PolyXmlErrorCode::ErrScalar
584 }
585}
586
587#[no_mangle]
588pub unsafe extern "C" fn polyxml_value_get_bool(
589 val: *const PolyXmlValue,
590 out_bool: *mut bool,
591) -> PolyXmlErrorCode {
592 if val.is_null() || out_bool.is_null() {
593 return PolyXmlErrorCode::ErrNullPtr;
594 }
595 if let Some(b) = (*val).0.as_bool() {
596 *out_bool = b;
597 PolyXmlErrorCode::Ok
598 } else {
599 PolyXmlErrorCode::ErrScalar
600 }
601}
602
603#[no_mangle]
604pub unsafe extern "C" fn polyxml_value_get_string(
605 val: *const PolyXmlValue,
606 out_str: *mut *const c_char,
607 out_len: *mut usize,
608) -> PolyXmlErrorCode {
609 if val.is_null() || out_str.is_null() || out_len.is_null() {
610 return PolyXmlErrorCode::ErrNullPtr;
611 }
612 if let Some(s) = (*val).0.as_str() {
613 *out_str = s.as_ptr() as *const c_char;
614 *out_len = s.len();
615 PolyXmlErrorCode::Ok
616 } else {
617 PolyXmlErrorCode::ErrScalar
618 }
619}
620
621#[no_mangle]
622pub unsafe extern "C" fn polyxml_value_get_list_len(
623 val: *const PolyXmlValue,
624 out_len: *mut usize,
625) -> PolyXmlErrorCode {
626 if val.is_null() || out_len.is_null() {
627 return PolyXmlErrorCode::ErrNullPtr;
628 }
629 if let Some(l) = (*val).0.as_list() {
630 *out_len = l.len();
631 PolyXmlErrorCode::Ok
632 } else {
633 PolyXmlErrorCode::ErrScalar
634 }
635}
636
637#[no_mangle]
638pub unsafe extern "C" fn polyxml_value_get_list_item(
639 val: *const PolyXmlValue,
640 idx: usize,
641) -> *const PolyXmlValue {
642 if val.is_null() {
643 return std::ptr::null();
644 }
645 if let Some(l) = (*val).0.as_list() {
646 if let Some(item) = l.get(idx) {
647 return item as *const PolyValue as *const PolyXmlValue;
648 }
649 }
650 std::ptr::null()
651}
652
653#[no_mangle]
654pub unsafe extern "C" fn polyxml_value_is_null(val: *const PolyXmlValue) -> bool {
655 if val.is_null() {
656 return true;
657 }
658 (*val).0.is_null()
659}
660
661#[no_mangle]
662pub unsafe extern "C" fn polyxml_value_free(val: *mut PolyXmlValue) {
663 if !val.is_null() {
664 drop(Box::from_raw(val));
665 }
666}
667
668#[no_mangle]
669pub extern "C" fn polyxml_version() -> *const c_char {
670 static VERSION: &[u8] = concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes();
671 VERSION.as_ptr() as *const c_char
672}
673
674#[cfg(test)]
675mod tests {
676 use super::*;
677 use std::ffi::CString;
678
679 #[test]
680 fn test_c_abi_namespaced_roundtrip() {
681 unsafe {
682 let name = CString::new("Order").unwrap();
683 let ns_order = CString::new("https://example.com/orders").unwrap();
684 let builder = polyxml_schema_builder_create(name.as_ptr());
685 polyxml_schema_builder_set_namespace(builder, ns_order.as_ptr());
686
687 let id_name = CString::new("id").unwrap();
688 let id_xml = CString::new("id").unwrap();
689 polyxml_schema_builder_add_field(
690 builder,
691 id_name.as_ptr(),
692 id_xml.as_ptr(),
693 PolyXmlFieldKind::Attribute,
694 PolyXmlScalarType::Int,
695 );
696
697 let item_name = CString::new("item").unwrap();
698 let item_xml = CString::new("item").unwrap();
699 let ns_item = CString::new("https://example.com/items").unwrap();
700 polyxml_schema_builder_add_field_with_namespace(
701 builder,
702 item_name.as_ptr(),
703 item_xml.as_ptr(),
704 PolyXmlFieldKind::Element,
705 PolyXmlScalarType::String,
706 ns_item.as_ptr(),
707 );
708
709 let schema = polyxml_schema_builder_build(builder);
710 assert!(!schema.is_null());
711
712 let xml = br#"<ns0:Order xmlns:ns0="https://example.com/orders" xmlns:ns1="https://example.com/items" id="101"><ns1:item>Gadget</ns1:item></ns0:Order>"#;
713 let mut val_ptr: *mut PolyXmlValue = std::ptr::null_mut();
714 let err = polyxml_deserialize(xml.as_ptr(), xml.len(), schema, &mut val_ptr);
715 assert_eq!(err as i32, PolyXmlErrorCode::Ok as i32);
716 assert!(!val_ptr.is_null());
717
718 let mut int_val = 0;
719 let id_field = polyxml_value_get_field(val_ptr, id_name.as_ptr());
720 assert!(!id_field.is_null());
721 polyxml_value_get_int(id_field, &mut int_val);
722 assert_eq!(int_val, 101);
723
724 let mut out_bytes: *mut u8 = std::ptr::null_mut();
725 let mut out_len = 0;
726 let p0 = CString::new("ord").unwrap();
727 let u0 = CString::new("https://example.com/orders").unwrap();
728 let prefixes = [p0.as_ptr()];
729 let uris = [u0.as_ptr()];
730
731 let serr = polyxml_serialize_with_options(
732 name.as_ptr(),
733 val_ptr,
734 schema,
735 0,
736 1,
737 prefixes.as_ptr(),
738 uris.as_ptr(),
739 1,
740 &mut out_bytes,
741 &mut out_len,
742 );
743 assert_eq!(serr as i32, PolyXmlErrorCode::Ok as i32);
744 assert!(!out_bytes.is_null());
745 let s = std::str::from_utf8(std::slice::from_raw_parts(out_bytes, out_len)).unwrap();
746 assert!(s.contains("xmlns:ord=\"https://example.com/orders\""));
747 assert!(s.contains("<ord:Order"));
748 assert!(s.contains("id=\"101\""));
749
750 polyxml_bytes_free(out_bytes, out_len);
751 polyxml_value_free(val_ptr);
752 polyxml_schema_free(schema);
753 }
754 }
755
756 #[test]
757 fn test_c_abi_nested_list_batch_roundtrip() {
758 unsafe {
759 let sensor_name = CString::new("Sensor").unwrap();
761 let s_builder = polyxml_schema_builder_create(sensor_name.as_ptr());
762 let id_str = CString::new("Id").unwrap();
763 let val_str = CString::new("Value").unwrap();
764 polyxml_schema_builder_add_field(
765 s_builder,
766 id_str.as_ptr(),
767 id_str.as_ptr(),
768 PolyXmlFieldKind::Element,
769 PolyXmlScalarType::String,
770 );
771 polyxml_schema_builder_add_field(
772 s_builder,
773 val_str.as_ptr(),
774 val_str.as_ptr(),
775 PolyXmlFieldKind::Element,
776 PolyXmlScalarType::Int,
777 );
778 let sensor_schema = polyxml_schema_builder_build(s_builder);
779 assert!(!sensor_schema.is_null());
780
781 let batch_name = CString::new("Batch").unwrap();
783 let b_builder = polyxml_schema_builder_create(batch_name.as_ptr());
784 let sensor_field_name = CString::new("Sensor").unwrap();
785 let sensor_xml_tag = CString::new("Sensor").unwrap();
786 polyxml_schema_builder_add_list_nested_field(
787 b_builder,
788 sensor_field_name.as_ptr(),
789 sensor_xml_tag.as_ptr(),
790 sensor_schema,
791 std::ptr::null(),
792 );
793 let batch_schema = polyxml_schema_builder_build(b_builder);
794 assert!(!batch_schema.is_null());
795
796 let xml = br#"<Batch><Sensor><Id>sensor-0</Id><Value>10</Value></Sensor><Sensor><Id>sensor-1</Id><Value>20</Value></Sensor></Batch>"#;
798 let mut val_ptr: *mut PolyXmlValue = std::ptr::null_mut();
799 let err = polyxml_deserialize(xml.as_ptr(), xml.len(), batch_schema, &mut val_ptr);
800 assert_eq!(err as i32, PolyXmlErrorCode::Ok as i32);
801 assert!(!val_ptr.is_null());
802
803 let list_val = polyxml_value_get_field(val_ptr, sensor_field_name.as_ptr());
805 assert!(!list_val.is_null());
806 let mut len = 0;
807 let len_err = polyxml_value_get_list_len(list_val, &mut len);
808 assert_eq!(len_err as i32, PolyXmlErrorCode::Ok as i32);
809 assert_eq!(len, 2);
810
811 let item0 = polyxml_value_get_list_item(list_val, 0);
813 assert!(!item0.is_null());
814 let id0 = polyxml_value_get_field(item0, id_str.as_ptr());
815 let mut id0_ptr: *const c_char = std::ptr::null();
816 let mut id0_len = 0;
817 polyxml_value_get_string(id0, &mut id0_ptr, &mut id0_len);
818 let id0_str =
819 std::str::from_utf8(std::slice::from_raw_parts(id0_ptr as *const u8, id0_len))
820 .unwrap();
821 assert_eq!(id0_str, "sensor-0");
822 let v0 = polyxml_value_get_field(item0, val_str.as_ptr());
823 let mut int0 = 0;
824 polyxml_value_get_int(v0, &mut int0);
825 assert_eq!(int0, 10);
826
827 let mut out_bytes: *mut u8 = std::ptr::null_mut();
829 let mut out_len = 0;
830 let serr = polyxml_serialize(
831 batch_name.as_ptr(),
832 val_ptr,
833 batch_schema,
834 0,
835 &mut out_bytes,
836 &mut out_len,
837 );
838 assert_eq!(serr as i32, PolyXmlErrorCode::Ok as i32);
839 let out_str =
840 std::str::from_utf8(std::slice::from_raw_parts(out_bytes, out_len)).unwrap();
841 assert_eq!(out_str, std::str::from_utf8(xml).unwrap());
842
843 polyxml_bytes_free(out_bytes, out_len);
844 polyxml_value_free(val_ptr);
845
846 let manual_batch = polyxml_value_create_record(batch_schema);
848 let manual_list = polyxml_value_create_list_with_capacity(2);
849 for i in 0..2 {
850 let s = polyxml_value_create_record(sensor_schema);
851 let id_val_str = format!("sensor-{}", i);
852 let c_id = polyxml_value_create_string(
853 id_val_str.as_ptr() as *const c_char,
854 id_val_str.len(),
855 );
856 polyxml_value_set_field(s, id_str.as_ptr(), c_id);
857 let c_val = polyxml_value_create_int((i + 1) * 10);
858 polyxml_value_set_field(s, val_str.as_ptr(), c_val);
859 polyxml_value_list_append(manual_list, s);
860 }
861 polyxml_value_set_field(manual_batch, sensor_field_name.as_ptr(), manual_list);
862
863 let mut manual_bytes: *mut u8 = std::ptr::null_mut();
864 let mut manual_len = 0;
865 let m_serr = polyxml_serialize(
866 batch_name.as_ptr(),
867 manual_batch,
868 batch_schema,
869 0,
870 &mut manual_bytes,
871 &mut manual_len,
872 );
873 assert_eq!(m_serr as i32, PolyXmlErrorCode::Ok as i32);
874 let manual_str =
875 std::str::from_utf8(std::slice::from_raw_parts(manual_bytes, manual_len)).unwrap();
876 assert_eq!(manual_str, std::str::from_utf8(xml).unwrap());
877
878 polyxml_bytes_free(manual_bytes, manual_len);
879 polyxml_value_free(manual_batch);
880 polyxml_schema_free(sensor_schema);
881 polyxml_schema_free(batch_schema);
882 }
883 }
884}