storekit/
subscription_status_stream.rs1use 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}