1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
macro_rules! ffi {
    // ffi!(self.destroy())
    (
        $self:ident
        . $method:ident ($($args:expr),* $(,)?)
    ) => {{
        use crate::panic_messages::NULL_PTR;
        log::trace!("FFI: {}", stringify!($method));
        let function = (*$self.core).$method.expect(NULL_PTR);
        function($self.core, $( $args ),*)
    }};

    // ffi!(self.get_application_manager().get_current_locale())
    (
        $self:ident
        . $get_manager:ident ()
        . $method:ident ($($args:expr),* $(,)?)
    ) => {{
        use crate::panic_messages::NULL_PTR;
        let manager = ffi!($self.$get_manager());
        log::trace!("FFI: .{}", stringify!($method));
        let function = (*manager).$method.expect(NULL_PTR);
        function(manager, $( $args ),*)
    }};

    // ffi!(self.get_activity_manager().accept_invite(user_id).and_then(callback))
    (
        $self:ident
        . $get_manager:ident ()
        . $method:ident ($($args:expr),* $(,)?)
        . and_then ($callback:expr $(,)?)
    ) => {{
        use crate::panic_messages::NULL_PTR;
        let manager = ffi!($self.$get_manager());
        log::trace!("FFI: .{}", stringify!($method));
        let function = (*manager).$method.expect(NULL_PTR);
        let (callback, sender) = $callback;
        let sender_ptr = Box::into_raw(Box::new(sender)) as *mut _;
        let fn_ptr = callback.c_fn();
        $self.register_callback(callback);
        function(manager, $( $args, )* sender_ptr, fn_ptr)
    }};
}

macro_rules! prevent_unwind {
    () => {
        let hook = std::panic::take_hook();

        std::panic::set_hook(Box::new(|info| {
            log::error!("panic across FFI bounds: {}", info);
            eprintln!(
                "
discord_game_sdk:
    The program has encountered a `panic` across FFI bounds, unwinding at this
    point would be undefined behavior, we will abort the process instead.
    Please report this issue to https://github.com/ldesgoui/discord_game_sdk
    Here is the panic message:

{}
            ",
                info
            );

            std::process::abort();
        }));

        scopeguard::defer_on_success!({
            std::panic::set_hook(hook);
        });
    };
}

macro_rules! get_str {
    ($doc:expr, $name:ident, $($field:tt)+) => {
        #[doc = $doc]
        #[doc = "\n\n## Cost\n\nString length is calculated every call"]
        pub fn $name(&self) -> &str {
            use crate::utils::cstr_to_str;

            let field = &(self.0).$($field)+;

            cstr_to_str(field)
        }
    };

    ($name:ident, $($field:tt)+) => {
        #[doc = "## Cost\n\nString length is calculated every call"]
        pub fn $name(&self) -> &str {
            use crate::utils::cstr_to_str;

            let field = &(self.0).$($field)+;

            cstr_to_str(field)
        }
    };
}

macro_rules! set_str {
    ($doc:expr, $name:ident, $($field:tt)+) => {
        #[doc = $doc]
        #[doc = "`value` must be valid UTF-8\n\n"]
        #[doc = "## Panics\n\n"]
        #[doc = "`value` must be smaller than the container it is being written to"]
        pub fn $name(&'_ mut self, value: impl AsRef<std::ffi::CStr>) -> &'_ mut Self {
            use crate::utils::slice_u8_to_i8;

            let bytes: &[i8] = slice_u8_to_i8(value.as_ref().to_bytes_with_nul());
            let field = &mut (self.0).$($field)+;

            debug_assert!(bytes.len() <= field.len());

            field[..bytes.len()].copy_from_slice(bytes);

            self
        }
    };

    ($name:ident, $($field:tt)+) => {
        #[doc = "`value` must be valid UTF-8\n\n"]
        #[doc = "## Panics\n\n"]
        #[doc = "`value` must be smaller than the container it is being written to"]
        pub fn $name(&'_ mut self, value: impl AsRef<std::ffi::CStr>) -> &'_ mut Self {
            use crate::utils::slice_u8_to_i8;

            let bytes: &[i8] = slice_u8_to_i8(value.as_ref().to_bytes_with_nul());
            let field = &mut (self.0).$($field)+;

            debug_assert!(bytes.len() <= field.len());

            field[..bytes.len()].copy_from_slice(bytes);

            self
        }
    };
}