arma_rs/
command.rs

1use crate::Context;
2use crate::call_context::{ArmaContextManager, CallContext, CallContextStackTrace};
3use crate::ext_result::IntoExtResult;
4use crate::flags::FeatureFlags;
5use crate::value::{FromArma, Value};
6
7type HandlerFunc = Box<
8    dyn Fn(
9        Context,
10        &ArmaContextManager,
11        *mut libc::c_char,
12        libc::size_t,
13        Option<*mut *mut i8>,
14        Option<libc::c_int>,
15    ) -> libc::c_int,
16>;
17
18#[doc(hidden)]
19/// A wrapper for `HandlerFunc`
20pub struct Handler {
21    /// The function to call
22    pub handler: HandlerFunc,
23}
24
25#[doc(hidden)]
26/// Create a new handler from a Factory
27pub fn fn_handler<C, I, R>(command: C) -> Handler
28where
29    C: Factory<I, R> + 'static,
30{
31    Handler {
32        handler: Box::new(
33            move |context: Context,
34                  acm: &ArmaContextManager,
35                  output: *mut libc::c_char,
36                  size: libc::size_t,
37                  args: Option<*mut *mut i8>,
38                  count: Option<libc::c_int>|
39                  -> libc::c_int {
40                unsafe { command.call(context, acm, output, size, args, count) }
41            },
42        ),
43    }
44}
45
46#[doc(hidden)]
47/// Execute a command
48pub trait Executor: 'static {
49    /// # Safety
50    /// This function is unsafe because it interacts with the C API.
51    unsafe fn call(
52        &self,
53        context: Context,
54        acm: &ArmaContextManager,
55        output: *mut libc::c_char,
56        size: libc::size_t,
57        args: Option<*mut *mut i8>,
58        count: Option<libc::c_int>,
59    );
60}
61
62#[doc(hidden)]
63/// A factory for creating a command handler.
64/// Creates a handler from any function that optionally takes a context and up to 12 arguments.
65/// The arguments must implement `FromArma`
66/// The return value must implement `IntoExtResult`
67pub trait Factory<A, R> {
68    /// # Safety
69    /// This function is unsafe because it interacts with the C API.
70    unsafe fn call(
71        &self,
72        context: Context,
73        acm: &ArmaContextManager,
74        output: *mut libc::c_char,
75        size: libc::size_t,
76        args: Option<*mut *mut i8>,
77        count: Option<libc::c_int>,
78    ) -> libc::c_int;
79}
80
81macro_rules! execute {
82    ($s:ident, $c:expr, $count:expr, $output:expr, $size:expr, $args:expr, ($( $vars:ident )*), ($( $param:ident, )*)) => {{
83        let count = $count.unwrap_or_else(|| 0);
84        if count != $c {
85            return format!("2{}", count).parse::<libc::c_int>().unwrap();
86        }
87        if $c == 0 {
88            handle_output_and_return(
89                ($s)($( $vars, )* $($param::from_arma("".to_string()).unwrap(),)*),
90                $output,
91                $size
92            )
93        } else {
94            #[allow(unused_variables, unused_mut)]
95            let mut argv: Vec<String> = {
96                let argv: &[*mut libc::c_char; $c] = &*($args.unwrap() as *const [*mut i8; $c]);
97                let mut argv = argv
98                    .to_vec()
99                    .into_iter()
100                    .map(|s| {
101                        std::ffi::CStr::from_ptr(s).to_string_lossy().to_string()
102                    })
103                    .collect::<Vec<String>>();
104                argv.reverse();
105                argv
106            };
107            #[allow(unused_variables, unused_mut)] // Caused by the 0 loop
108            let mut c = 0;
109            #[allow(unused_assignments, clippy::mixed_read_write_in_expression)]
110            handle_output_and_return(
111                {
112                    ($s)($( $vars, )* $(
113                        if let Ok(val) = $param::from_arma(argv.pop().unwrap()) {
114                            c += 1;
115                            val
116                        } else {
117                            return format!("3{}", c).parse::<libc::c_int>().unwrap()
118                        },
119                    )*)
120                },
121                $output,
122                $size
123            )
124        }
125    }};
126}
127
128macro_rules! factory_tuple ({ $c: expr, $($param:ident)* } => {
129    impl<$($param,)* ER> Executor for dyn Factory<($($param,)*), ER>
130    where
131        ER: 'static,
132        $($param: FromArma + 'static,)*
133    {
134        unsafe fn call(
135            &self,
136            context: Context,
137            acm: &ArmaContextManager,
138            output: *mut libc::c_char,
139            size: libc::size_t,
140            args: Option<*mut *mut i8>,
141            count: Option<libc::c_int>,
142        ) {
143            self.call(context, acm, output, size, args, count);
144        }
145    }
146
147    // No context
148    impl<Func, $($param,)* ER> Factory<($($param,)*), ER> for Func
149    where
150        ER: IntoExtResult + 'static,
151        Func: Fn($($param),*) -> ER,
152        $($param: FromArma,)*
153    {
154        #[allow(non_snake_case)]
155        unsafe fn call(&self, _: Context, _: &ArmaContextManager, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
156            let count = count.unwrap_or_else(|| 0);
157            if count != $c {
158                return format!("2{}", count).parse::<libc::c_int>().unwrap();
159            }
160            if $c == 0 {
161                handle_output_and_return(
162                    (self)($($param::from_arma("".to_string()).unwrap(),)*),
163                    output,
164                    size
165                )
166            } else {
167                #[allow(unused_variables, unused_mut)]
168                let mut argv: Vec<String> = {
169                    let argv: &[*mut libc::c_char; $c] = &*(args.unwrap() as *const [*mut i8; $c]);
170                    let mut argv = argv
171                        .to_vec()
172                        .into_iter()
173                        .map(|s| {
174                            std::ffi::CStr::from_ptr(s).to_string_lossy().to_string()
175                        })
176                        .collect::<Vec<String>>();
177                    argv.reverse();
178                    argv
179                };
180                #[allow(unused_variables, unused_mut)] // Caused by the 0 loop
181                let mut c = 0;
182                #[allow(unused_assignments, clippy::mixed_read_write_in_expression)]
183                handle_output_and_return(
184                    {
185                        (self)($(
186                            if let Ok(val) = $param::from_arma(argv.pop().unwrap()) {
187                                c += 1;
188                                val
189                            } else {
190                                return format!("3{}", c).parse::<libc::c_int>().unwrap()
191                            },
192                        )*)
193                    },
194                    output,
195                    size
196                )
197            }
198        }
199    }
200
201    // Context
202    impl<Func, $($param,)* ER> Factory<(Context, $($param,)*), ER> for Func
203    where
204        ER: IntoExtResult + 'static,
205        Func: Fn(Context, $($param),*) -> ER,
206        $($param: FromArma,)*
207    {
208        #[allow(non_snake_case)]
209        unsafe fn call(&self, context: Context, _: &ArmaContextManager, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
210            execute!(self, $c, count, output, size, args, (context), ($($param,)*))
211        }
212    }
213
214    // Call Context
215    impl<Func, $($param,)* ER> Factory<(CallContext, $($param,)*), ER> for Func
216    where
217        ER: IntoExtResult + 'static,
218        Func: Fn(CallContext, $($param),*) -> ER,
219        $($param: FromArma,)*
220    {
221        #[allow(non_snake_case)]
222        unsafe fn call(&self, _: Context, acm: &ArmaContextManager, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
223            crate::RVExtensionFeatureFlags = FeatureFlags::default().with_context_stack_trace(false).as_bits();
224            let call_context = acm.request().into_without_stack();
225            execute!(self, $c, count, output, size, args, (call_context), ($($param,)*))
226        }
227    }
228
229    // Call Context with Stack Trace
230    impl<Func, $($param,)* ER> Factory<(CallContextStackTrace, $($param,)*), ER> for Func
231    where
232        ER: IntoExtResult + 'static,
233        Func: Fn(CallContextStackTrace, $($param),*) -> ER,
234        $($param: FromArma,)*
235    {
236        #[allow(non_snake_case)]
237        unsafe fn call(&self, _: Context, acm: &ArmaContextManager, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
238            crate::RVExtensionFeatureFlags = FeatureFlags::default().with_context_stack_trace(true).as_bits();
239            let call_context = acm.request();
240            execute!(self, $c, count, output, size, args, (call_context), ($($param,)*))
241        }
242    }
243
244    // Context & Call Context
245    impl<Func, $($param,)* ER> Factory<(Context, CallContext, $($param,)*), ER> for Func
246    where
247        ER: IntoExtResult + 'static,
248        Func: Fn(Context, CallContext, $($param),*) -> ER,
249        $($param: FromArma,)*
250    {
251        #[allow(non_snake_case)]
252        unsafe fn call(&self, context: Context, acm: &ArmaContextManager, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
253            crate::RVExtensionFeatureFlags = FeatureFlags::default().with_context_stack_trace(false).as_bits();
254            let call_context = acm.request().into_without_stack();
255            execute!(self, $c, count, output, size, args, (context call_context), ($($param,)*))
256        }
257    }
258
259    // Context & Call Context with Stack Trace
260    impl<Func, $($param,)* ER> Factory<(Context, CallContextStackTrace, $($param,)*), ER> for Func
261    where
262        ER: IntoExtResult + 'static,
263        Func: Fn(Context, CallContextStackTrace, $($param),*) -> ER,
264        $($param: FromArma,)*
265    {
266        #[allow(non_snake_case)]
267        unsafe fn call(&self, context: Context, acm: &ArmaContextManager, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
268            crate::RVExtensionFeatureFlags = FeatureFlags::default().with_context_stack_trace(true).as_bits();
269            let call_context = acm.request();
270            execute!(self, $c, count, output, size, args, (context call_context), ($($param,)*))
271        }
272    }
273});
274
275unsafe fn handle_output_and_return<R>(
276    ret: R,
277    output: *mut libc::c_char,
278    size: libc::size_t,
279) -> libc::c_int
280where
281    R: IntoExtResult + 'static,
282{
283    let ret = ret.to_ext_result();
284    let ok = ret.is_ok();
285    if crate::write_cstr(
286        {
287            let value = match ret {
288                Ok(x) | Err(x) => x,
289            };
290            match value {
291                Value::String(s) => s,
292                v => v.to_string(),
293            }
294        },
295        output,
296        size,
297    )
298    .is_none()
299    {
300        4
301    } else if ok {
302        0
303    } else {
304        9
305    }
306}
307
308factory_tuple! { 0, }
309factory_tuple! { 1, A }
310factory_tuple! { 2, A B }
311factory_tuple! { 3, A B C }
312factory_tuple! { 4, A B C D }
313factory_tuple! { 5, A B C D E }
314factory_tuple! { 6, A B C D E F }
315factory_tuple! { 7, A B C D E F G }
316factory_tuple! { 8, A B C D E F G H }
317factory_tuple! { 9, A B C D E F G H I }
318factory_tuple! { 10, A B C D E F G H I J }
319factory_tuple! { 11, A B C D E F G H I J K }
320factory_tuple! { 12, A B C D E F G H I J K L }
321factory_tuple! { 13, A B C D E F G H I J K L M }
322factory_tuple! { 14, A B C D E F G H I J K L M N }
323factory_tuple! { 15, A B C D E F G H I J K L M N O }
324factory_tuple! { 16, A B C D E F G H I J K L M N O P }
325factory_tuple! { 17, A B C D E F G H I J K L M N O P Q }
326factory_tuple! { 18, A B C D E F G H I J K L M N O P Q R }
327factory_tuple! { 19, A B C D E F G H I J K L M N O P Q R S }
328factory_tuple! { 20, A B C D E F G H I J K L M N O P Q R S T }
329factory_tuple! { 21, A B C D E F G H I J K L M N O P Q R S T U }
330factory_tuple! { 22, A B C D E F G H I J K L M N O P Q R S T U V }
331factory_tuple! { 23, A B C D E F G H I J K L M N O P Q R S T U V W }
332factory_tuple! { 24, A B C D E F G H I J K L M N O P Q R S T U V W X }
333factory_tuple! { 25, A B C D E F G H I J K L M N O P Q R S T U V W X Y }
334factory_tuple! { 26, A B C D E F G H I J K L M N O P Q R S T U V W X Y Z }