Skip to main content

polyxml/
lib.rs

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
12// Opaque types
13pub 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                // Return borrowed reference as pointer
211                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}