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