Skip to main content

koicore_ffi/
lib.rs

1//! # KoiCore FFI
2//!
3//! This crate provides a C-compatible foreign function interface (FFI) for the KoiCore library,
4//! enabling C and other languages to interact with KoiLang parsing functionality.
5//!
6//! ## Features
7//!
8//! - Parse KoiLang text from various input sources (strings, files, custom callbacks)
9//! - Access and manipulate KoiLang commands and parameters
10//! - Handle composite data structures (lists and dictionaries)
11//! - Comprehensive error handling with detailed error information
12//!
13//! ## Modules
14//!
15//! - [`command`]: Functions for creating and manipulating KoiLang commands
16//! - [`parser`]: Functions for parsing KoiLang text and managing parser state
17//!
18//! ## Safety
19//!
20//! This FFI uses raw pointers and requires careful memory management. Users must:
21//! - Always check for null pointers before dereferencing
22//! - Properly free allocated objects using the provided `_Del` functions
23//! - Ensure thread safety when using the same parser from multiple threads
24//! - Follow the documentation for each function regarding parameter validation
25//!
26//! ## Example
27//!
28//! ```c
29//! #include "koicore.h"
30//!
31//! // Create a parser from a string
32//! const char* text = "#command param1 param2";
33//! struct KoiInputSource* input = KoiInputSource_FromString(text);
34//! struct KoiParserConfig* config = malloc(sizeof(struct KoiParserConfig));
35//! KoiParserConfig_Init(config);
36//!
37//! struct KoiParser* parser = KoiParser_New(input, config);
38//!
39//! // Parse commands
40//! struct KoiCommand* cmd = KoiParser_NextCommand(parser);
41//! if (cmd) {
42//!     // Process command
43//!     KoiCommand_Del(cmd);
44//! }
45//!
46//! // Clean up
47//! KoiParser_Del(parser);
48//! KoiInputSource_Del(input);
49//! free(config);
50//! ```
51
52pub mod command;
53pub mod parser;
54pub mod writer;
55
56#[cfg(test)]
57mod tests {
58    use crate::command::command::*;
59    use crate::command::dict::*;
60    use crate::command::list::*;
61    use crate::command::single::*;
62    use koicore::command::{Command, CompositeValue, Parameter, Value};
63    use std::ffi::CString;
64
65    #[test]
66    fn test_ffi_composite_list() {
67        unsafe {
68            let cmd_name = CString::new("test_cmd").unwrap();
69            let cmd = KoiCommand_New(cmd_name.as_ptr());
70
71            let list_name = CString::new("my_list").unwrap();
72            let list = KoiCompositeList_New(list_name.as_ptr());
73
74            KoiCompositeList_AddIntValue(list, 42);
75            KoiCommand_AddCompositeList(cmd, list);
76
77            let command = &*(cmd as *mut Command);
78            assert_eq!(command.name, "test_cmd");
79            assert_eq!(command.params.len(), 1);
80
81            if let Parameter::Composite(name, CompositeValue::List(values)) = &command.params[0] {
82                assert_eq!(name, "my_list");
83                assert_eq!(values.len(), 1);
84                assert_eq!(values[0], Value::Int(42));
85            } else {
86                panic!("Expected composite list parameter");
87            }
88
89            KoiCommand_Del(cmd);
90        }
91    }
92
93    #[test]
94    fn test_ffi_composite_dict() {
95        unsafe {
96            let cmd_name = CString::new("test_cmd").unwrap();
97            let cmd = KoiCommand_New(cmd_name.as_ptr());
98
99            let dict_name = CString::new("my_dict").unwrap();
100            let dict = KoiCompositeDict_New(dict_name.as_ptr());
101
102            let key = CString::new("key").unwrap();
103            KoiCompositeDict_SetIntValue(dict, key.as_ptr(), 123);
104            KoiCommand_AddCompositeDict(cmd, dict);
105
106            let command = &*(cmd as *mut Command);
107            assert_eq!(command.params.len(), 1);
108
109            if let Parameter::Composite(name, CompositeValue::Dict(entries)) = &command.params[0] {
110                assert_eq!(name, "my_dict");
111                assert_eq!(entries.len(), 1);
112                assert_eq!(entries[0].0, "key");
113                assert_eq!(entries[0].1, Value::Int(123));
114            } else {
115                panic!("Expected composite dict parameter");
116            }
117
118            KoiCommand_Del(cmd);
119        }
120    }
121
122    #[test]
123    fn test_ffi_composite_single() {
124        unsafe {
125            let cmd_name = CString::new("test_cmd").unwrap();
126            let cmd = KoiCommand_New(cmd_name.as_ptr());
127
128            let single_name = CString::new("my_single").unwrap();
129            let single = KoiCompositeSingle_New(single_name.as_ptr());
130
131            KoiCompositeSingle_SetIntValue(single, 114);
132            KoiCommand_AddCompositeSingle(cmd, single);
133
134            let command = &*(cmd as *mut Command);
135            assert_eq!(command.params.len(), 1);
136
137            if let Parameter::Composite(name, CompositeValue::Single(value)) = &command.params[0] {
138                assert_eq!(name, "my_single");
139                assert_eq!(*value, Value::Int(114));
140            } else {
141                panic!("Expected composite single parameter");
142            }
143
144            KoiCommand_Del(cmd);
145        }
146    }
147}