1use std::ffi::{CStr, CString};
2
3pub mod ffi;
5
6#[repr(C)]
7struct CallbackSite {
8 pub error: *mut libc::c_void,
9 pub include: *mut libc::c_void,
10}
11
12mod tcpp {
13 use std::os::raw::c_char;
14
15 use crate::{CallbackSite, ffi};
16
17 extern "C" {
18 pub(crate) fn process_with_specs(data: *mut c_char, callbacks: *const libc::c_void
19 , error: unsafe extern fn(*const CallbackSite, *const ffi::TErrorInfo)
20 , include: unsafe extern fn(*const CallbackSite, *const libc::c_char, bool) -> *const libc::c_void) -> *mut c_char;
21
22 pub(crate) fn process(data: *mut c_char) -> *mut c_char;
23 }
24}
25
26pub fn process(data: String) -> Option<String> {
33 let cstring = CString::new(data).ok()?.into_raw();
34 let result_raw = unsafe {
35 CStr::from_ptr(tcpp::process(cstring))
36 };
37 let _ = unsafe {
38 CString::from_raw(cstring) };
40 Some(result_raw.to_str().ok()?.to_owned())
41}
42
43unsafe extern "C" fn callback_error<T: FnMut(ffi::TErrorInfo)>(callbacks: *const CallbackSite, error: *const ffi::TErrorInfo) {
44 let callbacks = std::ptr::read(callbacks);
45 let closure = callbacks.error as *mut T;
46 (*closure)(std::ptr::read(error));
47}
48
49unsafe extern "C" fn callback_include<F: FnMut(String, bool) -> ffi::IInputStream>(callbacks: *const CallbackSite, file: *const libc::c_char, boolean: bool) -> *const libc::c_void {
50 let callbacks = std::ptr::read(callbacks);
51 let closure = callbacks.include as *mut F;
52 let file = CStr::from_ptr(file).to_str().unwrap().to_owned();
53 (*closure)(file, boolean).handler
54}
55
56pub fn process_with<T, F>(data: String, error: T, include: F) -> Option<String>
85 where T: FnMut(ffi::TErrorInfo),
86 F: FnMut(String, bool) -> ffi::IInputStream {
87 let cstring = CString::new(data).ok()?.into_raw();
88 let callbacks = Box::new(
89 CallbackSite {
90 include: Box::into_raw(Box::new(include)) as *mut _,
91 error: Box::into_raw(Box::new(error)) as *mut _,
92 });
93 let result_raw = unsafe {
94 CStr::from_ptr(tcpp::process_with_specs(cstring, Box::into_raw(callbacks) as *const _
95 , callback_error::<T>, callback_include::<F>))
96 };
97 let _ = unsafe {
98 CString::from_raw(cstring) };
100 Some(result_raw.to_str().ok()?.to_owned())
101}
102
103
104#[cfg(test)]
105mod tests {
106 use crate::*;
107 use crate::ffi::IInputStream;
108
109 #[test]
110 fn it_works() {
111 let str = String::from("\
112 #define TCPP_VALUE 10\n\
113 \
114 int main(int * argc,char ** argv) {\n\
115 #if (TCPP_VALUE < 5)\n\
116 printf(\"Hello tcpp TCPP_VALUE\");\n
117 #else\n\
118 printf(\"Hello Greater tcpp TCPP_VALUE\");\n\
119 #endif\n\
120 }\
121 ");
122 eprintln!("{:?}", process(str));
123 }
124
125 #[test]
126 fn reports_error() {
127 let str = String::from("\
128 #defined TCPP_VALUE 10\n\
129 \
130 int main(int * argc,char ** argv) {\n\
131 #if (TCPP_VALUE < 5)\n\
132 printf(\"Hello tcpp TCPP_VALUE\");\n
133 #else\n\
134 printf(\"Hello Greater tcpp TCPP_VALUE\");\n\
135 #endif\n\
136 }\
137 ");
138 eprintln!("{:?}", process_with(str, |err| {
139 eprintln!("error! {:?}", err.get_message());
140 }, |_, _| {
141 IInputStream::default()
142 }));
143 }
144}