1#![allow(clippy::missing_safety_doc)]
2use std::borrow::{Borrow, BorrowMut};
3use std::ffi::{CStr, CString};
4use std::mem;
5use std::os::raw::c_char;
6use std::path::PathBuf;
7use std::ptr::null_mut;
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::sync::mpsc::{channel, Receiver, Sender};
10use std::sync::Arc;
11use std::thread;
12
13use anyhow::{anyhow, Error};
14
15use crate::config::{read_vidx_list, ConfigBuilder, DEFAULT_VIDX_LIST};
16use crate::utils::set_last_error;
17use cmsis_pack::update::update;
18use cmsis_pack::update::DownloadProgress;
19
20pub struct UpdateReturn(pub(crate) Vec<PathBuf>);
21
22pub struct RunningUpdateContext {
23 pub(crate) thread_handle: thread::JoinHandle<Result<UpdateReturn, Error>>,
24 pub(crate) done_flag: Arc<AtomicBool>,
25 pub(crate) result_stream: Receiver<DownloadUpdate>,
26}
27
28#[repr(C)]
29pub struct DownloadUpdate {
30 pub is_size: bool,
31 pub size: usize,
32}
33
34pub(crate) struct DownloadSender(Sender<DownloadUpdate>);
35
36impl DownloadSender {
37 pub(crate) fn from_sender(from: Sender<DownloadUpdate>) -> Self {
38 DownloadSender(from)
39 }
40}
41
42impl DownloadProgress for DownloadSender {
43 fn size(&self, size: usize) {
44 let _ = self.0.send(DownloadUpdate {
45 is_size: true,
46 size,
47 });
48 }
49
50 fn progress(&self, _: usize) {
51 }
53
54 fn complete(&self) {
55 let _ = self.0.send(DownloadUpdate {
56 is_size: false,
57 size: 0,
58 });
59 }
60
61 fn for_file(&self, _: &str) -> Self {
62 DownloadSender(self.0.clone())
63 }
64}
65
66pub enum UpdatePoll {
69 Running(RunningUpdateContext),
70 Complete(Result<UpdateReturn, Error>),
71 Drained,
72}
73
74impl UpdateReturn {
75 pub fn from_vec(inner: Vec<PathBuf>) -> Self {
76 UpdateReturn(inner)
77 }
78
79 pub fn iter(&self) -> impl Iterator<Item = &PathBuf> {
80 self.0.iter()
81 }
82}
83
84cffi! {
85 fn update_pdsc_index(
86 pack_store: *const c_char,
87 vidx_list: *const c_char,
88 ) -> Result<*mut UpdatePoll> {
89 let conf_bld = ConfigBuilder::default();
90 let conf_bld = if !pack_store.is_null() {
91 let pstore = unsafe { CStr::from_ptr(pack_store) }.to_string_lossy();
92 conf_bld.with_pack_store(pstore.into_owned())
93 } else {
94 conf_bld
95 };
96 let vidx_list = if !vidx_list.is_null() {
97 let vlist = unsafe { CStr::from_ptr(vidx_list) }.to_string_lossy();
98 read_vidx_list(vlist.as_ref().as_ref())
99 } else {
100 DEFAULT_VIDX_LIST.iter().map(|s| String::from(*s)).collect()
101 };
102 let conf = conf_bld.build()?;
103 let (send, recv) = channel();
104 let done_flag = Arc::new(AtomicBool::new(false));
105 let threads_done_flag = done_flag.clone();
106 let thread = thread::Builder::new()
107 .name("update".to_string())
108 .spawn(move || {
109 let res = update(
110 &conf,
111 vidx_list,
112 DownloadSender::from_sender(send)
113 ).map(UpdateReturn);
114 threads_done_flag.store(true, Ordering::Release);
115 res
116 })?;
117 Ok(Box::into_raw(Box::new(UpdatePoll::Running(RunningUpdateContext{
118 thread_handle: thread,
119 done_flag,
120 result_stream: recv,
121 }))))
122 }
123}
124
125#[no_mangle]
126pub unsafe extern "C" fn update_pdsc_poll(ptr: *mut UpdatePoll) -> bool {
127 if !ptr.is_null() {
128 with_from_raw!(let mut boxed = ptr,{
129 let (ret, next_state) = match mem::replace(boxed.borrow_mut(), UpdatePoll::Drained) {
130 UpdatePoll::Complete(inner) => (true, UpdatePoll::Complete(inner)),
131 UpdatePoll::Drained => (true, UpdatePoll::Drained),
132 UpdatePoll::Running(cont) => {
133 if cont.done_flag.load(Ordering::Acquire) {
134 let response = cont.thread_handle.join();
135 let response = match response {
136 Ok(inner) => inner,
137 Err(_) => Err(anyhow!("thread paniced"))
138 };
139 (true, UpdatePoll::Complete(response))
140 } else {
141 (false, UpdatePoll::Running(cont))
142 }
143 }
144 };
145 let _ = mem::replace(boxed.borrow_mut(), next_state);
146 ret
147 })
148 } else {
149 false
150 }
151}
152
153#[no_mangle]
154pub unsafe extern "C" fn update_pdsc_get_status(ptr: *mut UpdatePoll) -> *mut DownloadUpdate {
155 if !ptr.is_null() {
156 with_from_raw!(let boxed = ptr,{
157 match boxed.borrow() {
158 UpdatePoll::Complete(_) => null_mut(),
159 UpdatePoll::Drained => null_mut(),
160 UpdatePoll::Running(ref cont) => {
161 let response = cont.result_stream.try_recv();
162 match response {
163 Ok(inner) => Box::into_raw(Box::new(inner)),
164 Err(_) => null_mut()
165 }
166 }
167 }
168 })
169 } else {
170 null_mut()
171 }
172}
173
174cffi! {
175 fn update_pdsc_status_free(ptr: *mut DownloadUpdate) {
176 if !ptr.is_null() {
177 drop(unsafe { Box::from_raw(ptr) })
178 }
179 }
180}
181
182#[no_mangle]
183pub unsafe extern "C" fn update_pdsc_result(ptr: *mut UpdatePoll) -> *mut UpdateReturn {
184 if !ptr.is_null() {
185 with_from_raw!(let mut boxed = ptr,{
186 let (ret, next_state) = match mem::replace(boxed.borrow_mut(), UpdatePoll::Drained) {
187 UpdatePoll::Complete(inner) => (Some(inner), UpdatePoll::Drained),
188 UpdatePoll::Drained => (None, UpdatePoll::Drained),
189 UpdatePoll::Running(cont) => (None, UpdatePoll::Running(cont))
190 };
191 let _ = mem::replace(boxed.borrow_mut(), next_state);
192 match ret {
193 Some(Ok(inner)) => Box::into_raw(Box::new(inner)),
194 Some(Err(inner)) => {
195 println!("{:?}", inner);
196 set_last_error(inner);
197 null_mut()
198 },
199 None => null_mut()
200 }
201 })
202 } else {
203 null_mut()
204 }
205}
206
207#[no_mangle]
208pub extern "C" fn update_pdsc_index_new() -> *mut UpdateReturn {
209 Box::into_raw(Box::new(UpdateReturn(Vec::new())))
210}
211
212cffi! {
213 fn update_pdsc_index_next(ptr: *mut UpdateReturn) -> Result<*const c_char> {
214 if !ptr.is_null() {
215 with_from_raw!(let mut boxed = ptr, {
216 if let Some(osstr) = boxed.0.pop().map(|p| p.into_os_string()){
217 match osstr.to_str() {
218 Some(osstr) => {
219 Ok(CString::new(osstr).map(|cstr| cstr.into_raw())?)
220 },
221 None => Err(anyhow!("Could not create a C string from a Rust String"))
222 }
223 } else {
224 Ok(null_mut())
225 }
226 })
227 } else {
228 Err(anyhow!("update pdsc index next called with null"))
229 }
230 }
231}
232
233cffi! {
234 fn update_pdsc_index_push(ptr: *mut UpdateReturn, cstr: *mut c_char) -> Result<()> {
235 if !ptr.is_null() && !cstr.is_null() {
236 with_from_raw!(let mut boxed = ptr, {
237 let pstore = unsafe { CStr::from_ptr(cstr) }.to_string_lossy();
238 boxed.0.push(pstore.into_owned().into());
239 Ok(())
240 })
241 } else {
242 Err(anyhow!("update pdsc index push called with null"))
243 }
244 }
245}
246
247cffi! {
248 fn cstring_free(ptr: *mut c_char) {
249 if !ptr.is_null() {
250 drop(unsafe { CString::from_raw(ptr) })
251 }
252 }
253}
254
255cffi! {
256 fn update_pdsc_index_free(ptr: *mut UpdateReturn) {
257 if !ptr.is_null() {
258 drop(unsafe { Box::from_raw(ptr) })
259 }
260 }
261}