1#![doc = include_str!("../README.md")]
2use std::ffi::{CStr, CString};
7use std::os::raw::c_char;
8use std::ptr;
9
10use styx_tree::{BuildError, Document, Object, Payload, Sequence, Value};
11
12pub struct StyxDocument {
14 inner: Document,
15}
16
17pub struct StyxValue {
19 #[allow(dead_code)]
20 inner: *const Value,
21}
22
23pub struct StyxObject {
25 #[allow(dead_code)]
26 inner: *const Object,
27}
28
29pub struct StyxSequence {
31 #[allow(dead_code)]
32 inner: *const Sequence,
33}
34
35#[repr(C)]
37pub struct StyxParseResult {
38 pub document: *mut StyxDocument,
40 pub error: *mut c_char,
42}
43
44#[repr(C)]
46pub enum StyxPayloadKind {
47 None,
49 Scalar,
51 Sequence,
53 Object,
55}
56
57#[unsafe(no_mangle)]
69pub unsafe extern "C" fn styx_parse(source: *const c_char) -> StyxParseResult {
70 if source.is_null() {
71 let error = CString::new("source is null").unwrap();
72 return StyxParseResult {
73 document: ptr::null_mut(),
74 error: error.into_raw(),
75 };
76 }
77
78 let source = match unsafe { CStr::from_ptr(source) }.to_str() {
79 Ok(s) => s,
80 Err(_) => {
81 let error = CString::new("source is not valid UTF-8").unwrap();
82 return StyxParseResult {
83 document: ptr::null_mut(),
84 error: error.into_raw(),
85 };
86 }
87 };
88
89 match Document::parse(source) {
90 Ok(doc) => {
91 let boxed = Box::new(StyxDocument { inner: doc });
92 StyxParseResult {
93 document: Box::into_raw(boxed),
94 error: ptr::null_mut(),
95 }
96 }
97 Err(e) => {
98 let error_msg = format_error(&e);
99 let error =
100 CString::new(error_msg).unwrap_or_else(|_| CString::new("unknown error").unwrap());
101 StyxParseResult {
102 document: ptr::null_mut(),
103 error: error.into_raw(),
104 }
105 }
106 }
107}
108
109fn format_error(e: &BuildError) -> String {
110 match e {
111 BuildError::UnexpectedEvent(msg) => format!("unexpected event: {}", msg),
112 BuildError::UnclosedStructure => "unclosed structure".to_string(),
113 BuildError::EmptyDocument => "empty document".to_string(),
114 BuildError::Parse(kind, span) => {
115 format!("parse error at {}-{}: {}", span.start, span.end, kind)
116 }
117 }
118}
119
120#[unsafe(no_mangle)]
126pub unsafe extern "C" fn styx_free_document(doc: *mut StyxDocument) {
127 if !doc.is_null() {
128 drop(unsafe { Box::from_raw(doc) });
129 }
130}
131
132#[unsafe(no_mangle)]
137pub unsafe extern "C" fn styx_free_string(s: *mut c_char) {
138 if !s.is_null() {
139 drop(unsafe { CString::from_raw(s) });
140 }
141}
142
143#[unsafe(no_mangle)]
153pub unsafe extern "C" fn styx_document_root(doc: *const StyxDocument) -> *const StyxObject {
154 if doc.is_null() {
155 return ptr::null();
156 }
157 let doc = unsafe { &*doc };
158 &doc.inner.root as *const Object as *const StyxObject
159}
160
161#[unsafe(no_mangle)]
168pub unsafe extern "C" fn styx_document_get(
169 doc: *const StyxDocument,
170 path: *const c_char,
171) -> *const StyxValue {
172 if doc.is_null() || path.is_null() {
173 return ptr::null();
174 }
175 let doc = unsafe { &*doc };
176 let path = match unsafe { CStr::from_ptr(path) }.to_str() {
177 Ok(s) => s,
178 Err(_) => return ptr::null(),
179 };
180 match doc.inner.get(path) {
181 Some(value) => value as *const Value as *const StyxValue,
182 None => ptr::null(),
183 }
184}
185
186#[unsafe(no_mangle)]
195pub unsafe extern "C" fn styx_value_payload_kind(value: *const StyxValue) -> StyxPayloadKind {
196 if value.is_null() {
197 return StyxPayloadKind::None;
198 }
199 let value = unsafe { &*(value as *const Value) };
200 match &value.payload {
201 None => StyxPayloadKind::None,
202 Some(Payload::Scalar(_)) => StyxPayloadKind::Scalar,
203 Some(Payload::Sequence(_)) => StyxPayloadKind::Sequence,
204 Some(Payload::Object(_)) => StyxPayloadKind::Object,
205 }
206}
207
208#[unsafe(no_mangle)]
213pub unsafe extern "C" fn styx_value_is_unit(value: *const StyxValue) -> bool {
214 if value.is_null() {
215 return false;
216 }
217 let value = unsafe { &*(value as *const Value) };
218 value.is_unit()
219}
220
221#[unsafe(no_mangle)]
227pub unsafe extern "C" fn styx_value_tag(value: *const StyxValue) -> *mut c_char {
228 if value.is_null() {
229 return ptr::null_mut();
230 }
231 let value = unsafe { &*(value as *const Value) };
232 match value.tag_name() {
233 Some(name) => CString::new(name)
234 .map(|s| s.into_raw())
235 .unwrap_or(ptr::null_mut()),
236 None => ptr::null_mut(),
237 }
238}
239
240#[unsafe(no_mangle)]
246pub unsafe extern "C" fn styx_value_scalar(value: *const StyxValue) -> *mut c_char {
247 if value.is_null() {
248 return ptr::null_mut();
249 }
250 let value = unsafe { &*(value as *const Value) };
251 match value.scalar_text() {
252 Some(text) => CString::new(text)
253 .map(|s| s.into_raw())
254 .unwrap_or(ptr::null_mut()),
255 None => ptr::null_mut(),
256 }
257}
258
259#[unsafe(no_mangle)]
265pub unsafe extern "C" fn styx_value_as_object(value: *const StyxValue) -> *const StyxObject {
266 if value.is_null() {
267 return ptr::null();
268 }
269 let value = unsafe { &*(value as *const Value) };
270 match &value.payload {
271 Some(Payload::Object(obj)) => obj as *const Object as *const StyxObject,
272 _ => ptr::null(),
273 }
274}
275
276#[unsafe(no_mangle)]
282pub unsafe extern "C" fn styx_value_as_sequence(value: *const StyxValue) -> *const StyxSequence {
283 if value.is_null() {
284 return ptr::null();
285 }
286 let value = unsafe { &*(value as *const Value) };
287 match &value.payload {
288 Some(Payload::Sequence(seq)) => seq as *const Sequence as *const StyxSequence,
289 _ => ptr::null(),
290 }
291}
292
293#[unsafe(no_mangle)]
300pub unsafe extern "C" fn styx_value_get(
301 value: *const StyxValue,
302 path: *const c_char,
303) -> *const StyxValue {
304 if value.is_null() || path.is_null() {
305 return ptr::null();
306 }
307 let value = unsafe { &*(value as *const Value) };
308 let path = match unsafe { CStr::from_ptr(path) }.to_str() {
309 Ok(s) => s,
310 Err(_) => return ptr::null(),
311 };
312 match value.get(path) {
313 Some(v) => v as *const Value as *const StyxValue,
314 None => ptr::null(),
315 }
316}
317
318#[unsafe(no_mangle)]
327pub unsafe extern "C" fn styx_object_len(obj: *const StyxObject) -> usize {
328 if obj.is_null() {
329 return 0;
330 }
331 let obj = unsafe { &*(obj as *const Object) };
332 obj.len()
333}
334
335#[unsafe(no_mangle)]
342pub unsafe extern "C" fn styx_object_get(
343 obj: *const StyxObject,
344 key: *const c_char,
345) -> *const StyxValue {
346 if obj.is_null() || key.is_null() {
347 return ptr::null();
348 }
349 let obj = unsafe { &*(obj as *const Object) };
350 let key = match unsafe { CStr::from_ptr(key) }.to_str() {
351 Ok(s) => s,
352 Err(_) => return ptr::null(),
353 };
354 match obj.get(key) {
355 Some(value) => value as *const Value as *const StyxValue,
356 None => ptr::null(),
357 }
358}
359
360#[unsafe(no_mangle)]
367pub unsafe extern "C" fn styx_object_key_at(
368 obj: *const StyxObject,
369 index: usize,
370) -> *const StyxValue {
371 if obj.is_null() {
372 return ptr::null();
373 }
374 let obj = unsafe { &*(obj as *const Object) };
375 match obj.entries.get(index) {
376 Some(entry) => &entry.key as *const Value as *const StyxValue,
377 None => ptr::null(),
378 }
379}
380
381#[unsafe(no_mangle)]
388pub unsafe extern "C" fn styx_object_value_at(
389 obj: *const StyxObject,
390 index: usize,
391) -> *const StyxValue {
392 if obj.is_null() {
393 return ptr::null();
394 }
395 let obj = unsafe { &*(obj as *const Object) };
396 match obj.entries.get(index) {
397 Some(entry) => &entry.value as *const Value as *const StyxValue,
398 None => ptr::null(),
399 }
400}
401
402#[unsafe(no_mangle)]
411pub unsafe extern "C" fn styx_sequence_len(seq: *const StyxSequence) -> usize {
412 if seq.is_null() {
413 return 0;
414 }
415 let seq = unsafe { &*(seq as *const Sequence) };
416 seq.len()
417}
418
419#[unsafe(no_mangle)]
426pub unsafe extern "C" fn styx_sequence_get(
427 seq: *const StyxSequence,
428 index: usize,
429) -> *const StyxValue {
430 if seq.is_null() {
431 return ptr::null();
432 }
433 let seq = unsafe { &*(seq as *const Sequence) };
434 match seq.get(index) {
435 Some(value) => value as *const Value as *const StyxValue,
436 None => ptr::null(),
437 }
438}