1use crate::sys;
4use std::{
5 ffi::{self, CStr, CString},
6 ptr, slice,
7};
8
9type NetworkCallback = Box<dyn FnOnce(NetworkRequest, i32)>;
10
11pub struct NetworkRequestBuilder<'a> {
13 url: CString,
14 headers: Vec<CString>,
15 data: Option<&'a mut [u8]>,
16 callback: Option<Box<NetworkCallback>>,
17}
18impl<'a> NetworkRequestBuilder<'a> {
19 pub fn new(url: &str) -> Option<Self> {
21 Some(Self {
22 url: CString::new(url).ok()?,
23 headers: vec![],
24 data: None,
25 callback: None,
26 })
27 }
28
29 pub fn with_header(mut self, header: &str) -> Option<Self> {
31 self.headers.push(CString::new(header).ok()?);
32 Some(self)
33 }
34
35 pub fn with_data(mut self, data: &'a mut [u8]) -> Self {
37 self.data = Some(data);
38 self
39 }
40
41 pub fn with_callback(mut self, callback: impl FnOnce(NetworkRequest, i32) + 'static) -> Self {
44 self.callback = Some(Box::new(Box::new(callback)));
45 self
46 }
47
48 pub fn get(self) -> Option<NetworkRequest> {
50 self.do_request(None, sys::fsNetworkHttpRequestGet)
51 }
52
53 pub fn post(self, post_field: &str) -> Option<NetworkRequest> {
55 let post_field = CString::new(post_field).unwrap();
56 self.do_request(Some(post_field), sys::fsNetworkHttpRequestPost)
57 }
58
59 pub fn put(self) -> Option<NetworkRequest> {
61 self.do_request(None, sys::fsNetworkHttpRequestPut)
62 }
63
64 fn do_request(
65 mut self,
66 post_field: Option<CString>,
67 request: unsafe extern "C" fn(
68 *const ::std::os::raw::c_char,
69 *mut sys::FsNetworkHttpRequestParam,
70 sys::HttpRequestCallback,
71 *mut ::std::os::raw::c_void,
72 ) -> sys::FsNetworkRequestId,
73 ) -> Option<NetworkRequest> {
74 let raw_post_field = post_field
76 .as_ref()
77 .map_or(ptr::null_mut(), |f| f.as_c_str().as_ptr() as *mut i8);
78
79 let mut headers = self
82 .headers
83 .iter_mut()
84 .map(|h| h.as_ptr() as *mut i8)
85 .collect::<Vec<_>>();
86 let data_len = self.data.as_ref().map_or(0, |d| d.len());
87 let mut params = sys::FsNetworkHttpRequestParam {
88 postField: raw_post_field,
89 headerOptions: headers.as_mut_ptr(),
90 headerOptionsSize: headers.len() as std::os::raw::c_uint,
91 data: self
92 .data
93 .as_mut()
94 .map_or(ptr::null_mut(), |d| d.as_mut_ptr()),
95 dataSize: data_len as std::os::raw::c_uint,
96 };
97 let callback_data = self.callback.map_or(ptr::null_mut(), Box::into_raw) as *mut _;
98 let request_id = unsafe {
99 request(
100 self.url.as_ptr(),
101 &mut params as *mut sys::FsNetworkHttpRequestParam,
102 Some(Self::c_wrapper),
103 callback_data,
104 )
105 };
106
107 if request_id == 0 {
108 let _: Box<NetworkCallback> = unsafe { Box::from_raw(callback_data as *mut _) };
110 None
111 } else {
112 Some(NetworkRequest(request_id))
113 }
114 }
115
116 extern "C" fn c_wrapper(
117 request_id: sys::FsNetworkRequestId,
118 status_code: i32,
119 user_data: *mut ffi::c_void,
120 ) {
121 if !user_data.is_null() {
122 let callback: Box<NetworkCallback> = unsafe { Box::from_raw(user_data as *mut _) };
123 callback(NetworkRequest(request_id), status_code);
124 }
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
130pub enum NetworkRequestState {
131 Invalid,
132 New,
133 WaitingForData,
134 DataReady,
135 Failed,
136}
137impl From<sys::FsNetworkHttpRequestState> for NetworkRequestState {
138 fn from(value: sys::FsNetworkHttpRequestState) -> Self {
139 match value {
140 sys::FsNetworkHttpRequestState_FS_NETWORK_HTTP_REQUEST_STATE_INVALID => Self::Invalid,
141 sys::FsNetworkHttpRequestState_FS_NETWORK_HTTP_REQUEST_STATE_NEW => Self::New,
142 sys::FsNetworkHttpRequestState_FS_NETWORK_HTTP_REQUEST_STATE_WAITING_FOR_DATA => {
143 Self::WaitingForData
144 }
145 sys::FsNetworkHttpRequestState_FS_NETWORK_HTTP_REQUEST_STATE_DATA_READY => {
146 Self::DataReady
147 }
148 sys::FsNetworkHttpRequestState_FS_NETWORK_HTTP_REQUEST_STATE_FAILED => Self::Failed,
149 _ => panic!("Unknown request state"),
150 }
151 }
152}
153
154#[derive(Clone, Copy)]
156pub struct NetworkRequest(sys::FsNetworkRequestId);
157impl NetworkRequest {
158 pub fn cancel(&self) -> bool {
160 unsafe { sys::fsNetworkHttpCancelRequest(self.0) }
161 }
162
163 pub fn data_size(&self) -> usize {
165 unsafe { sys::fsNetworkHttpRequestGetDataSize(self.0) as usize }
166 }
167
168 pub fn data(&self) -> Option<Vec<u8>> {
170 let data_size = self.data_size();
171 if data_size == 0 {
172 return None;
173 }
174 unsafe {
175 let data = sys::fsNetworkHttpRequestGetData(self.0);
176 if data.is_null() {
177 None
178 } else {
179 let result = slice::from_raw_parts(data, data_size).to_owned();
180 libc::free(data as *mut ffi::c_void);
181 Some(result)
182 }
183 }
184 }
185
186 pub fn error_code(&self) -> i32 {
188 unsafe { sys::fsNetworkHttpRequestGetErrorCode(self.0) }
189 }
190
191 pub fn state(&self) -> NetworkRequestState {
193 unsafe { sys::fsNetworkHttpRequestGetState(self.0).into() }
194 }
195
196 pub fn header_section(&self, section: &str) -> Option<String> {
198 let section = CString::new(section).ok()?;
199 unsafe {
200 let a = sys::fsNetworkHttpRequestGetHeaderSection(self.0, section.as_ptr());
201 if a.is_null() {
202 None
203 } else {
204 let result = CStr::from_ptr(a).to_str().ok().map(|s| s.to_owned());
205 libc::free(a as *mut ffi::c_void);
206 result
207 }
208 }
209 }
210}