summaryrefslogtreecommitdiffhomepage
path: root/mullvad-ios/src/api_client/completion.rs
blob: aa1a00bb07e96cb6ba500a563d3f92899972dbcd (plain)
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
use std::sync::{Arc, Mutex};

use super::response::SwiftMullvadApiResponse;

unsafe extern "C" {
    /// Maps to `mullvadApiCompletionFinish` on Swift side to facilitate callback based completion flow when doing
    /// network calls through Mullvad API on Rust side.
    ///
    /// # Safety
    ///
    /// `response` must be pointing to a valid instance of `SwiftMullvadApiResponse`.
    ///
    /// `completion_cookie` must be pointing to a valid instance of `CompletionCookie`. `CompletionCookie` is safe
    /// because the pointer in `MullvadApiCompletion` is valid for the lifetime of the process where this type is
    /// intended to be used.
    pub fn mullvad_api_completion_finish(
        response: SwiftMullvadApiResponse,
        completion_cookie: CompletionCookie,
    );
}

#[repr(C)]
pub struct CompletionCookie {
    inner: *mut std::ffi::c_void,
}
/// SAFETY: Access to `CompletionCookie` should always be done through a `SwiftCompletionHandler`
/// It is safe to be used and sent from any threads.
unsafe impl Send for CompletionCookie {}
impl CompletionCookie {
    /// `inner` must be pointing to a valid instance of Swift object `MullvadApiCompletion`.
    pub unsafe fn new(inner: *mut std::ffi::c_void) -> Self {
        Self { inner }
    }
}

#[derive(Clone)]
pub struct SwiftCompletionHandler {
    inner: Arc<Mutex<Option<CompletionCookie>>>,
}

impl SwiftCompletionHandler {
    pub fn new(cookie: CompletionCookie) -> Self {
        Self {
            inner: Arc::new(Mutex::new(Some(cookie))),
        }
    }

    // This function makes sure that completion is done only once.
    pub fn finish(&self, response: SwiftMullvadApiResponse) {
        let Ok(mut maybe_cookie) = self.inner.lock() else {
            log::error!("Response handler panicked");
            return;
        };

        let Some(cookie) = maybe_cookie.take() else {
            return;
        };

        // SAFETY: See safety notes for `mullvad_api_completion_finish`
        unsafe { mullvad_api_completion_finish(response, cookie) };
    }
}