Skip to main content

storekit/
subscription_status_stream.rs

1use core::ffi::c_void;
2use core::ptr;
3use std::ptr::NonNull;
4use std::time::Duration;
5
6use serde::Deserialize;
7
8use crate::error::StoreKitError;
9use crate::ffi;
10use crate::private::{duration_to_timeout_ms, error_from_status, parse_json_ptr};
11use crate::subscription_info::{SubscriptionStatus, SubscriptionStatusPayload};
12
13#[derive(Debug, Clone)]
14pub struct SubscriptionGroupStatuses {
15    pub group_id: String,
16    pub statuses: Vec<SubscriptionStatus>,
17}
18
19impl SubscriptionStatus {
20    pub fn updates() -> Result<SubscriptionStatusStream, StoreKitError> {
21        SubscriptionStatusStream::new()
22    }
23
24    pub fn all() -> Result<SubscriptionGroupStatusStream, StoreKitError> {
25        SubscriptionGroupStatusStream::new()
26    }
27}
28
29#[derive(Debug)]
30pub struct SubscriptionStatusStream {
31    handle: NonNull<c_void>,
32    finished: bool,
33}
34
35impl Drop for SubscriptionStatusStream {
36    fn drop(&mut self) {
37        unsafe { ffi::sk_subscription_status_stream_release(self.handle.as_ptr()) };
38    }
39}
40
41impl SubscriptionStatusStream {
42    fn new() -> Result<Self, StoreKitError> {
43        let mut error_message = ptr::null_mut();
44        let handle = unsafe { ffi::sk_subscription_status_stream_create(&mut error_message) };
45        let handle = NonNull::new(handle)
46            .ok_or_else(|| unsafe { error_from_status(ffi::status::UNKNOWN, error_message) })?;
47        Ok(Self {
48            handle,
49            finished: false,
50        })
51    }
52
53    pub const fn is_finished(&self) -> bool {
54        self.finished
55    }
56
57    #[allow(clippy::should_implement_trait)]
58    pub fn next(&mut self) -> Result<Option<SubscriptionStatus>, StoreKitError> {
59        self.next_timeout(Duration::from_secs(30))
60    }
61
62    pub fn next_timeout(&mut self, timeout: Duration) -> Result<Option<SubscriptionStatus>, StoreKitError> {
63        let mut status_json = ptr::null_mut();
64        let mut error_message = ptr::null_mut();
65        let status = unsafe {
66            ffi::sk_subscription_status_stream_next(
67                self.handle.as_ptr(),
68                duration_to_timeout_ms(timeout),
69                &mut status_json,
70                &mut error_message,
71            )
72        };
73
74        match status {
75            ffi::status::OK => {
76                let payload = unsafe {
77                    parse_json_ptr::<SubscriptionStatusPayload>(status_json, "subscription status update")
78                }?;
79                payload.into_subscription_status().map(Some)
80            }
81            ffi::status::END_OF_STREAM => {
82                self.finished = true;
83                Ok(None)
84            }
85            ffi::status::TIMED_OUT => Ok(None),
86            _ => Err(unsafe { error_from_status(status, error_message) }),
87        }
88    }
89}
90
91#[derive(Debug)]
92pub struct SubscriptionGroupStatusStream {
93    handle: NonNull<c_void>,
94    finished: bool,
95}
96
97impl Drop for SubscriptionGroupStatusStream {
98    fn drop(&mut self) {
99        unsafe { ffi::sk_subscription_group_status_stream_release(self.handle.as_ptr()) };
100    }
101}
102
103impl SubscriptionGroupStatusStream {
104    fn new() -> Result<Self, StoreKitError> {
105        let mut error_message = ptr::null_mut();
106        let handle = unsafe { ffi::sk_subscription_group_status_stream_create(&mut error_message) };
107        let handle = NonNull::new(handle)
108            .ok_or_else(|| unsafe { error_from_status(ffi::status::UNKNOWN, error_message) })?;
109        Ok(Self {
110            handle,
111            finished: false,
112        })
113    }
114
115    pub const fn is_finished(&self) -> bool {
116        self.finished
117    }
118
119    #[allow(clippy::should_implement_trait)]
120    pub fn next(&mut self) -> Result<Option<SubscriptionGroupStatuses>, StoreKitError> {
121        self.next_timeout(Duration::from_secs(30))
122    }
123
124    pub fn next_timeout(
125        &mut self,
126        timeout: Duration,
127    ) -> Result<Option<SubscriptionGroupStatuses>, StoreKitError> {
128        let mut payload_json = ptr::null_mut();
129        let mut error_message = ptr::null_mut();
130        let status = unsafe {
131            ffi::sk_subscription_group_status_stream_next(
132                self.handle.as_ptr(),
133                duration_to_timeout_ms(timeout),
134                &mut payload_json,
135                &mut error_message,
136            )
137        };
138
139        match status {
140            ffi::status::OK => {
141                let payload = unsafe {
142                    parse_json_ptr::<SubscriptionGroupStatusesPayload>(
143                        payload_json,
144                        "subscription group statuses",
145                    )
146                }?;
147                payload.into_group_statuses().map(Some)
148            }
149            ffi::status::END_OF_STREAM => {
150                self.finished = true;
151                Ok(None)
152            }
153            ffi::status::TIMED_OUT => Ok(None),
154            _ => Err(unsafe { error_from_status(status, error_message) }),
155        }
156    }
157}
158
159#[derive(Debug, Deserialize)]
160struct SubscriptionGroupStatusesPayload {
161    #[serde(rename = "groupID")]
162    group_id: String,
163    statuses: Vec<SubscriptionStatusPayload>,
164}
165
166impl SubscriptionGroupStatusesPayload {
167    fn into_group_statuses(self) -> Result<SubscriptionGroupStatuses, StoreKitError> {
168        Ok(SubscriptionGroupStatuses {
169            group_id: self.group_id,
170            statuses: self
171                .statuses
172                .into_iter()
173                .map(SubscriptionStatusPayload::into_subscription_status)
174                .collect::<Result<Vec<_>, _>>()?,
175        })
176    }
177}