Skip to main content

samp_sdk/
call.rs

1//! Typed calls into Pawn publics — [`Amx::call_public`].
2//!
3//! [`exec_public!`] covers the same ground, but a macro cannot be passed
4//! around, stored, or written generically, and it makes the caller mark which
5//! arguments need heap allocation (`expr => string`). This module carries that
6//! decision in the type instead: a `&str` allocates and copies, an `i32` goes
7//! straight into a cell, and the call site says only what it means.
8//!
9//! ```rust,no_run
10//! # use samp_sdk::amx::Amx;
11//! # fn example(amx: &Amx) -> samp_sdk::error::AmxResult<()> {
12//! amx.call_public("OnPlayerScored", (7, "headshot", 1.5))?;
13//! amx.call_public("OnRoundEnd", ())?;
14//! # Ok(()) }
15//! ```
16//!
17//! Pawn reads the arguments in declaration order, which means the VM wants them
18//! pushed backwards; the tuple implementations do that, so the call site lists
19//! them the way the Pawn signature does.
20//!
21//! Types accepted as arguments: the integer primitives, `f32` (as `Float:`),
22//! `bool`, `&str` and `String` (allocated on the AMX heap and passed as
23//! `const arg[]`), `&[i32]` and `&Vec<i32>` (likewise, as `arg[]`), and the
24//! cell types `AmxString`, `Buffer` and `Ref<T>` that a native already receives.
25//! Anything else — a custom [`AmxCell`] — keeps using [`exec_public!`].
26//!
27//! [`exec_public!`]: crate::exec_public
28//! [`AmxCell`]: crate::cell::AmxCell
29
30use crate::amx::{Allocator, Amx};
31use crate::cell::{AmxPrimitive, AmxString, Buffer, Ref};
32use crate::error::AmxResult;
33
34/// A value that can be passed to a Pawn public by [`Amx::call_public`].
35///
36/// Implemented for the types listed in the [module docs](self); the `allocator`
37/// is there for the ones that need memory inside the VM, and is ignored by the
38/// ones that fit in a cell.
39pub trait PublicArg<'amx> {
40    /// Pushes `self` onto the AMX stack.
41    ///
42    /// # Errors
43    /// Propagates the VM's error when the push fails, or when the allocation an
44    /// argument needs does not fit in the AMX heap.
45    fn push_to(self, amx: &'amx Amx, allocator: &Allocator<'amx>) -> AmxResult<()>;
46}
47
48/// Cell-sized values: pushed as they are.
49macro_rules! impl_cell_arg {
50    ($($type:ty),* $(,)?) => {
51        $(
52            impl<'amx> PublicArg<'amx> for $type {
53                #[inline]
54                fn push_to(self, amx: &'amx Amx, _allocator: &Allocator<'amx>) -> AmxResult<()> {
55                    amx.push(self)
56                }
57            }
58        )*
59    };
60}
61
62impl_cell_arg!(i8, u8, i16, u16, i32, u32, usize, isize, f32, bool);
63
64impl<'amx> PublicArg<'amx> for AmxString<'amx> {
65    #[inline]
66    fn push_to(self, amx: &'amx Amx, _allocator: &Allocator<'amx>) -> AmxResult<()> {
67        amx.push(self)
68    }
69}
70
71impl<'amx> PublicArg<'amx> for Buffer<'amx> {
72    #[inline]
73    fn push_to(self, amx: &'amx Amx, _allocator: &Allocator<'amx>) -> AmxResult<()> {
74        amx.push(self)
75    }
76}
77
78impl<'amx, T: AmxPrimitive> PublicArg<'amx> for Ref<'amx, T> {
79    #[inline]
80    fn push_to(self, amx: &'amx Amx, _allocator: &Allocator<'amx>) -> AmxResult<()> {
81        amx.push(self)
82    }
83}
84
85/// Rust strings are copied into the AMX heap and passed as `const arg[]`.
86impl<'amx> PublicArg<'amx> for &str {
87    #[inline]
88    fn push_to(self, amx: &'amx Amx, allocator: &Allocator<'amx>) -> AmxResult<()> {
89        let string = allocator.allot_string(self)?;
90        amx.push(string)
91    }
92}
93
94impl<'amx> PublicArg<'amx> for &String {
95    #[inline]
96    fn push_to(self, amx: &'amx Amx, allocator: &Allocator<'amx>) -> AmxResult<()> {
97        self.as_str().push_to(amx, allocator)
98    }
99}
100
101/// Cell slices are copied into the AMX heap and passed as `arg[]`.
102impl<'amx> PublicArg<'amx> for &[i32] {
103    #[inline]
104    fn push_to(self, amx: &'amx Amx, allocator: &Allocator<'amx>) -> AmxResult<()> {
105        let array = allocator.allot_array(self)?;
106        amx.push(array)
107    }
108}
109
110impl<'amx> PublicArg<'amx> for &Vec<i32> {
111    #[inline]
112    fn push_to(self, amx: &'amx Amx, allocator: &Allocator<'amx>) -> AmxResult<()> {
113        self.as_slice().push_to(amx, allocator)
114    }
115}
116
117/// An argument list for [`Amx::call_public`] — a tuple of [`PublicArg`]s, up to
118/// twelve of them, or `()` for a public that takes none.
119pub trait PublicArgs<'amx> {
120    /// Pushes every argument, last one first, as the VM expects.
121    ///
122    /// # Errors
123    /// Propagates the first failure from [`PublicArg::push_to`].
124    fn push_all(self, amx: &'amx Amx, allocator: &Allocator<'amx>) -> AmxResult<()>;
125}
126
127impl PublicArgs<'_> for () {
128    #[inline]
129    fn push_all(self, _amx: &Amx, _allocator: &Allocator<'_>) -> AmxResult<()> {
130        Ok(())
131    }
132}
133
134/// Generates the tuple implementations. Each one pushes in reverse: the VM pops
135/// the first declared argument last.
136macro_rules! impl_args_tuple {
137    ($(($($name:ident),+)),* $(,)?) => {
138        $(
139            #[allow(non_snake_case)]
140            impl<'amx, $($name: PublicArg<'amx>),+> PublicArgs<'amx> for ($($name,)+) {
141                #[inline]
142                fn push_all(self, amx: &'amx Amx, allocator: &Allocator<'amx>) -> AmxResult<()> {
143                    let ($($name,)+) = self;
144                    impl_args_tuple!(@push amx, allocator, $($name),+);
145                    Ok(())
146                }
147            }
148        )*
149    };
150    (@push $amx:ident, $al:ident, $head:ident) => {
151        $head.push_to($amx, $al)?;
152    };
153    (@push $amx:ident, $al:ident, $head:ident, $($tail:ident),+) => {
154        impl_args_tuple!(@push $amx, $al, $($tail),+);
155        $head.push_to($amx, $al)?;
156    };
157}
158
159impl_args_tuple!(
160    (A),
161    (A, B),
162    (A, B, C),
163    (A, B, C, D),
164    (A, B, C, D, E),
165    (A, B, C, D, E, F),
166    (A, B, C, D, E, F, G),
167    (A, B, C, D, E, F, G, H),
168    (A, B, C, D, E, F, G, H, I),
169    (A, B, C, D, E, F, G, H, I, J),
170    (A, B, C, D, E, F, G, H, I, J, K),
171    (A, B, C, D, E, F, G, H, I, J, K, L),
172);