cmsis_cffi/
pack_index.rs

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        /* not implemented */
52    }
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
66/* Turns out that this enum is not UnwindSafe, so you may not call panic_unwind
67 * on any closure that takes an UpdatePoll as an argument */
68pub 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}