mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Implement new lib-c sources API
This commit is contained in:
@@ -27,6 +27,69 @@ enum vpkc_update_check_t
|
|||||||
typedef int8_t vpkc_update_check_t;
|
typedef int8_t vpkc_update_check_t;
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opaque type for a Velopack UpdateSource. Must be freed with `vpkc_free_update_source`.
|
||||||
|
*/
|
||||||
|
typedef void vpkc_update_source_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User delegate for to fetch a release feed. This function should return the raw JSON string of the release.json feed.
|
||||||
|
*/
|
||||||
|
typedef const char *(*vpkc_release_feed_delegate_t)(void *p_user_data, const char *psz_releases_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An individual Velopack asset, could refer to an asset on-disk or in a remote package feed.
|
||||||
|
*/
|
||||||
|
typedef struct vpkc_asset_t {
|
||||||
|
/**
|
||||||
|
* The name or Id of the package containing this release.
|
||||||
|
*/
|
||||||
|
char *PackageId;
|
||||||
|
/**
|
||||||
|
* The version of this release.
|
||||||
|
*/
|
||||||
|
char *Version;
|
||||||
|
/**
|
||||||
|
* The type of asset (eg. "Full" or "Delta").
|
||||||
|
*/
|
||||||
|
char *Type;
|
||||||
|
/**
|
||||||
|
* The filename of the update package containing this release.
|
||||||
|
*/
|
||||||
|
char *FileName;
|
||||||
|
/**
|
||||||
|
* The SHA1 checksum of the update package containing this release.
|
||||||
|
*/
|
||||||
|
char *SHA1;
|
||||||
|
/**
|
||||||
|
* The SHA256 checksum of the update package containing this release.
|
||||||
|
*/
|
||||||
|
char *SHA256;
|
||||||
|
/**
|
||||||
|
* The size in bytes of the update package containing this release.
|
||||||
|
*/
|
||||||
|
uint64_t Size;
|
||||||
|
/**
|
||||||
|
* The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string.
|
||||||
|
*/
|
||||||
|
char *NotesMarkdown;
|
||||||
|
/**
|
||||||
|
* The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string.
|
||||||
|
*/
|
||||||
|
char *NotesHtml;
|
||||||
|
} vpkc_asset_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User delegate for downloading an asset file. This function is expected to download the provided asset
|
||||||
|
* to the provided local file path. Througout, you can use the progress callback to write progress reports.
|
||||||
|
* The function should return true if the download was successful, false otherwise.
|
||||||
|
* Progress
|
||||||
|
*/
|
||||||
|
typedef bool (*vpkc_download_asset_delegate_t)(void *p_user_data,
|
||||||
|
const struct vpkc_asset_t *p_asset,
|
||||||
|
const char *psz_local_path,
|
||||||
|
size_t progress_callback_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options to customise the behaviour of UpdateManager.
|
* Options to customise the behaviour of UpdateManager.
|
||||||
*/
|
*/
|
||||||
@@ -85,48 +148,6 @@ typedef struct vpkc_locator_config_t {
|
|||||||
*/
|
*/
|
||||||
typedef void vpkc_update_manager_t;
|
typedef void vpkc_update_manager_t;
|
||||||
|
|
||||||
/**
|
|
||||||
* An individual Velopack asset, could refer to an asset on-disk or in a remote package feed.
|
|
||||||
*/
|
|
||||||
typedef struct vpkc_asset_t {
|
|
||||||
/**
|
|
||||||
* The name or Id of the package containing this release.
|
|
||||||
*/
|
|
||||||
char *PackageId;
|
|
||||||
/**
|
|
||||||
* The version of this release.
|
|
||||||
*/
|
|
||||||
char *Version;
|
|
||||||
/**
|
|
||||||
* The type of asset (eg. "Full" or "Delta").
|
|
||||||
*/
|
|
||||||
char *Type;
|
|
||||||
/**
|
|
||||||
* The filename of the update package containing this release.
|
|
||||||
*/
|
|
||||||
char *FileName;
|
|
||||||
/**
|
|
||||||
* The SHA1 checksum of the update package containing this release.
|
|
||||||
*/
|
|
||||||
char *SHA1;
|
|
||||||
/**
|
|
||||||
* The SHA256 checksum of the update package containing this release.
|
|
||||||
*/
|
|
||||||
char *SHA256;
|
|
||||||
/**
|
|
||||||
* The size in bytes of the update package containing this release.
|
|
||||||
*/
|
|
||||||
uint64_t Size;
|
|
||||||
/**
|
|
||||||
* The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string.
|
|
||||||
*/
|
|
||||||
char *NotesMarkdown;
|
|
||||||
/**
|
|
||||||
* The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string.
|
|
||||||
*/
|
|
||||||
char *NotesHtml;
|
|
||||||
} vpkc_asset_t;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds information about the current version and pending updates, such as how many there are, and access to release notes.
|
* Holds information about the current version and pending updates, such as how many there are, and access to release notes.
|
||||||
*/
|
*/
|
||||||
@@ -164,6 +185,38 @@ typedef void (*vpkc_log_callback_t)(void *p_user_data,
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new FileSource update source for a given file path.
|
||||||
|
*/
|
||||||
|
vpkc_update_source_t *vpkc_new_source_file(const char *psz_file_path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new HttpSource update source for a given HTTP URL.
|
||||||
|
*/
|
||||||
|
vpkc_update_source_t *vpkc_new_source_http_url(const char *psz_http_url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new _CUSTOM_ update source with user-provided callbacks to fetch release feeds and download assets.
|
||||||
|
* You can report download progress using `vpkc_source_report_progress`. Note that the callbacks must be valid
|
||||||
|
* for the lifetime of any UpdateManager's that use this source. You should call `vpkc_free_source` to free the source,
|
||||||
|
* but note that if the source is still in use by an UpdateManager, it will not be freed until the UpdateManager is freed.
|
||||||
|
* Therefore to avoid possible issues, it is recommended to create this type of source once for the lifetime of your application.
|
||||||
|
*/
|
||||||
|
vpkc_update_source_t *vpkc_new_source_custom_callback(vpkc_release_feed_delegate_t cb_release_feed,
|
||||||
|
vpkc_download_asset_delegate_t cb_download_entry,
|
||||||
|
void *p_user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a progress update to the callback with the specified ID. This is used by custom
|
||||||
|
* update sources created with `vpkc_new_source_custom_callback` to report download progress.
|
||||||
|
*/
|
||||||
|
void vpkc_source_report_progress(size_t progress_callback_id, int16_t progress);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees a vpkc_update_source_t instance.
|
||||||
|
*/
|
||||||
|
void vpkc_free_source(vpkc_update_source_t *p_source);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new UpdateManager instance.
|
* Create a new UpdateManager instance.
|
||||||
* @param urlOrPath Location of the update server or path to the local update directory.
|
* @param urlOrPath Location of the update server or path to the local update directory.
|
||||||
@@ -176,28 +229,15 @@ bool vpkc_new_update_manager(const char *psz_url_or_path,
|
|||||||
vpkc_update_manager_t **p_manager);
|
vpkc_update_manager_t **p_manager);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new UpdateManager instance.
|
* Create a new UpdateManager instance with a custom UpdateSource.
|
||||||
|
* @param urlOrPath Location of the update server or path to the local update directory.
|
||||||
* @param options Optional extra configuration for update manager.
|
* @param options Optional extra configuration for update manager.
|
||||||
* @param locator Override the default locator configuration (usually used for testing / mocks).
|
* @param locator Override the default locator configuration (usually used for testing / mocks).
|
||||||
* @param callback to override the default update source
|
|
||||||
* (AutoSource). Retrieve the list of available remote releases from
|
|
||||||
* the package source. These releases can subsequently be downloaded
|
|
||||||
* with cb_download_release_entry.
|
|
||||||
* @param callback to override the default update source
|
|
||||||
* (AutoSource). Download the specified VelopackAsset to the provided
|
|
||||||
* local file path.
|
|
||||||
* @param parameter to the callbacks to override the default update
|
|
||||||
* source (AutoSource). It's the user's responsibilty to ensure that
|
|
||||||
* it's safe to send and share it across threads
|
|
||||||
*/
|
*/
|
||||||
bool vpkc_new_custom_update_manager(const char *(*cb_get_release_feed)(const char*, void*),
|
bool vpkc_new_update_manager_with_source(vpkc_update_source_t *p_source,
|
||||||
bool (*cb_download_release_entry)(const char*,
|
struct vpkc_update_options_t *p_options,
|
||||||
const char*,
|
struct vpkc_locator_config_t *p_locator,
|
||||||
void*),
|
vpkc_update_manager_t **p_manager);
|
||||||
void *p_user_data,
|
|
||||||
struct vpkc_update_options_t *p_options,
|
|
||||||
struct vpkc_locator_config_t *p_locator,
|
|
||||||
vpkc_update_manager_t **p_manager);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the currently installed version of the app.
|
* Returns the currently installed version of the app.
|
||||||
|
|||||||
77
src/lib-cpp/src/csource.rs
Normal file
77
src/lib-cpp/src/csource.rs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
use crate::types::*;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use libc::{c_void, size_t};
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
ffi::CString,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
mpsc::Sender,
|
||||||
|
RwLock,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use velopack::{bundle::Manifest, sources::UpdateSource, Error, VelopackAsset, VelopackAssetFeed};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref PROGRESS_CALLBACKS: RwLock<HashMap<size_t, Sender<i16>>> = RwLock::new(HashMap::new());
|
||||||
|
static ref PROGRESS_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn report_csource_progress(callback_id: size_t, progress: i16) {
|
||||||
|
let progress_callbacks = PROGRESS_CALLBACKS.read().unwrap();
|
||||||
|
if let Some(sender) = progress_callbacks.get(&callback_id) {
|
||||||
|
let _ = sender.send(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CCallbackUpdateSource {
|
||||||
|
pub p_user_data: *mut c_void,
|
||||||
|
pub cb_get_release_feed: vpkc_release_feed_delegate_t,
|
||||||
|
pub cb_download_release_entry: vpkc_download_asset_delegate_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for CCallbackUpdateSource {}
|
||||||
|
unsafe impl Sync for CCallbackUpdateSource {}
|
||||||
|
|
||||||
|
impl UpdateSource for CCallbackUpdateSource {
|
||||||
|
fn get_release_feed(&self, channel: &str, _: &Manifest) -> Result<VelopackAssetFeed, Error> {
|
||||||
|
let releases_name = format!("releases.{}.json", channel);
|
||||||
|
let releases_name_cstr = CString::new(releases_name).unwrap();
|
||||||
|
let json_cstr_ptr = (self.cb_get_release_feed)(self.p_user_data, releases_name_cstr.as_ptr());
|
||||||
|
let json = c_to_string_opt(json_cstr_ptr)
|
||||||
|
.ok_or(Error::Generic("User vpkc_release_feed_delegate_t returned a null pointer instead of an asset feed".to_string()))?;
|
||||||
|
let feed: VelopackAssetFeed = serde_json::from_str(&json)?;
|
||||||
|
Ok(feed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download_release_entry(&self, asset: &VelopackAsset, local_file: &str, progress_sender: Option<Sender<i16>>) -> Result<(), Error> {
|
||||||
|
let local_file_cstr = CString::new(local_file).unwrap();
|
||||||
|
let asset_ptr: *mut vpkc_asset_t = std::ptr::null_mut();
|
||||||
|
unsafe { allocate_velopackasset(asset.clone(), asset_ptr) };
|
||||||
|
|
||||||
|
let progress_callback_id = PROGRESS_ID.fetch_add(1, Ordering::SeqCst);
|
||||||
|
if let Some(progress_sender) = &progress_sender {
|
||||||
|
let _ = progress_sender.send(0);
|
||||||
|
PROGRESS_CALLBACKS.write().unwrap().insert(progress_callback_id, progress_sender.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let success = (self.cb_download_release_entry)(self.p_user_data, asset_ptr, local_file_cstr.as_ptr(), progress_callback_id);
|
||||||
|
|
||||||
|
unsafe { free_velopackasset(asset_ptr) };
|
||||||
|
|
||||||
|
if let Some(sender) = PROGRESS_CALLBACKS.write().unwrap().remove(&progress_callback_id) {
|
||||||
|
let _ = sender.send(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !success {
|
||||||
|
return Err(Error::Generic("User vpkc_download_asset_delegate_t returned false to indicate download failed".to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_boxed(&self) -> Box<dyn UpdateSource> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,14 +4,76 @@
|
|||||||
|
|
||||||
mod statics;
|
mod statics;
|
||||||
use statics::*;
|
use statics::*;
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
use types::*;
|
use types::*;
|
||||||
|
mod csource;
|
||||||
|
use csource::*;
|
||||||
|
mod raw;
|
||||||
|
use raw::*;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail};
|
use anyhow::{anyhow, bail};
|
||||||
use libc::{c_char, c_void, size_t};
|
use libc::{c_char, c_void, size_t};
|
||||||
use std::{sync::mpsc::Sender, ffi::{CStr, CString}};
|
use std::{ffi::CString, ptr};
|
||||||
use velopack::{bundle, sources, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp, VelopackAsset, VelopackAssetFeed};
|
use velopack::{sources, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp};
|
||||||
|
|
||||||
|
/// Create a new FileSource update source for a given file path.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn vpkc_new_source_file(psz_file_path: *const c_char) -> *mut vpkc_update_source_t {
|
||||||
|
if let Some(update_path) = c_to_string_opt(psz_file_path) {
|
||||||
|
UpdateSourceRawPtr::new(Box::new(sources::FileSource::new(update_path)))
|
||||||
|
} else {
|
||||||
|
ptr::null_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new HttpSource update source for a given HTTP URL.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn vpkc_new_source_http_url(psz_http_url: *const c_char) -> *mut vpkc_update_source_t {
|
||||||
|
if let Some(update_url) = c_to_string_opt(psz_http_url) {
|
||||||
|
UpdateSourceRawPtr::new(Box::new(sources::FileSource::new(update_url)))
|
||||||
|
} else {
|
||||||
|
ptr::null_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new _CUSTOM_ update source with user-provided callbacks to fetch release feeds and download assets.
|
||||||
|
/// You can report download progress using `vpkc_source_report_progress`. Note that the callbacks must be valid
|
||||||
|
/// for the lifetime of any UpdateManager's that use this source. You should call `vpkc_free_source` to free the source,
|
||||||
|
/// but note that if the source is still in use by an UpdateManager, it will not be freed until the UpdateManager is freed.
|
||||||
|
/// Therefore to avoid possible issues, it is recommended to create this type of source once for the lifetime of your application.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn vpkc_new_source_custom_callback(
|
||||||
|
cb_release_feed: vpkc_release_feed_delegate_t,
|
||||||
|
cb_download_entry: vpkc_download_asset_delegate_t,
|
||||||
|
p_user_data: *mut c_void,
|
||||||
|
) -> *mut vpkc_update_source_t {
|
||||||
|
let cb_release_feed = cb_release_feed.to_option();
|
||||||
|
let cb_download_entry = cb_download_entry.to_option();
|
||||||
|
if cb_release_feed.is_none() || cb_download_entry.is_none() {
|
||||||
|
return ptr::null_mut();
|
||||||
|
}
|
||||||
|
|
||||||
|
let source = CCallbackUpdateSource {
|
||||||
|
p_user_data,
|
||||||
|
cb_get_release_feed: cb_release_feed.unwrap(),
|
||||||
|
cb_download_release_entry: cb_download_entry.unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
UpdateSourceRawPtr::new(Box::new(source))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a progress update to the callback with the specified ID. This is used by custom
|
||||||
|
/// update sources created with `vpkc_new_source_custom_callback` to report download progress.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn vpkc_source_report_progress(progress_callback_id: size_t, progress: i16) {
|
||||||
|
report_csource_progress(progress_callback_id, progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees a vpkc_update_source_t instance.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn vpkc_free_source(p_source: *mut vpkc_update_source_t) {
|
||||||
|
UpdateSourceRawPtr::free(p_source);
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new UpdateManager instance.
|
/// Create a new UpdateManager instance.
|
||||||
/// @param urlOrPath Location of the update server or path to the local update directory.
|
/// @param urlOrPath Location of the update server or path to the local update directory.
|
||||||
@@ -35,90 +97,22 @@ pub extern "C" fn vpkc_new_update_manager(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
/// Create a new UpdateManager instance with a custom UpdateSource.
|
||||||
/// Retrieves available updates using a custom method. This is
|
/// @param urlOrPath Location of the update server or path to the local update directory.
|
||||||
/// intended to be used from C and C++ code when `AutoSource` is
|
|
||||||
/// inadequate.
|
|
||||||
struct CCallbackUpdateSource {
|
|
||||||
/// Opaque data that's passed to the callbacks
|
|
||||||
p_user_data: *mut c_void,
|
|
||||||
/// Callback function that returns the requrested asset feed JSON as a string
|
|
||||||
cb_get_release_feed: extern "C" fn(psz_releases_name: *const c_char, p_user_data: *mut c_void) -> *const c_char,
|
|
||||||
/// Callback function that downloads the requested asset file to a local path
|
|
||||||
cb_download_release_entry: extern "C" fn(psz_asset_file_name: *const c_char, psz_local_file: *const c_char, p_user_data: *mut c_void) -> bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for CCallbackUpdateSource {}
|
|
||||||
unsafe impl Sync for CCallbackUpdateSource {}
|
|
||||||
|
|
||||||
impl sources::UpdateSource for CCallbackUpdateSource {
|
|
||||||
fn get_release_feed(&self, channel: &str, _: &bundle::Manifest) -> Result<VelopackAssetFeed, VelopackError> {
|
|
||||||
let releases_name = format!("releases.{}.json", channel);
|
|
||||||
let releases_name_cstr = CString::new(releases_name).unwrap();
|
|
||||||
let json_cstr_ptr = (self.cb_get_release_feed)(releases_name_cstr.as_ptr(), self.p_user_data);
|
|
||||||
if json_cstr_ptr.is_null() {
|
|
||||||
Err(VelopackError::Generic("Failed to fetch releases JSON".to_owned()))
|
|
||||||
} else {
|
|
||||||
let json_cstr = unsafe { CStr::from_ptr(json_cstr_ptr) };
|
|
||||||
let feed: VelopackAssetFeed = serde_json::from_str(json_cstr.to_str().unwrap())?;
|
|
||||||
Ok(feed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn download_release_entry(&self, asset: &VelopackAsset, local_file: &str, progress_sender: Option<Sender<i16>>) -> Result<(), VelopackError> {
|
|
||||||
if let Some(progress_sender) = &progress_sender {
|
|
||||||
let _ = progress_sender.send(50);
|
|
||||||
}
|
|
||||||
let asset_file_name_cstr = CString::new(asset.FileName.as_str()).unwrap();
|
|
||||||
let local_file_cstr = CString::new(local_file).unwrap();
|
|
||||||
let success = (self.cb_download_release_entry)(asset_file_name_cstr.as_ptr(), local_file_cstr.as_ptr(), self.p_user_data);
|
|
||||||
if success {
|
|
||||||
if let Some(progress_sender) = &progress_sender {
|
|
||||||
let _ = progress_sender.send(100);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(VelopackError::Generic("Failed to download asset file".to_owned()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clone_boxed(&self) -> Box<dyn sources::UpdateSource> {
|
|
||||||
Box::new(self.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Create a new UpdateManager instance.
|
|
||||||
/// @param options Optional extra configuration for update manager.
|
/// @param options Optional extra configuration for update manager.
|
||||||
/// @param locator Override the default locator configuration (usually used for testing / mocks).
|
/// @param locator Override the default locator configuration (usually used for testing / mocks).
|
||||||
/// @param callback to override the default update source
|
|
||||||
/// (AutoSource). Retrieve the list of available remote releases from
|
|
||||||
/// the package source. These releases can subsequently be downloaded
|
|
||||||
/// with cb_download_release_entry.
|
|
||||||
/// @param callback to override the default update source
|
|
||||||
/// (AutoSource). Download the specified VelopackAsset to the provided
|
|
||||||
/// local file path.
|
|
||||||
/// @param parameter to the callbacks to override the default update
|
|
||||||
/// source (AutoSource). It's the user's responsibilty to ensure that
|
|
||||||
/// it's safe to send and share it across threads
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn vpkc_new_custom_update_manager(
|
pub extern "C" fn vpkc_new_update_manager_with_source(
|
||||||
cb_get_release_feed: extern "C" fn(*const c_char, *mut c_void) -> *const c_char,
|
p_source: *mut vpkc_update_source_t,
|
||||||
cb_download_release_entry: extern "C" fn(*const c_char, *const c_char, *mut c_void) -> bool,
|
|
||||||
p_user_data: *mut c_void,
|
|
||||||
p_options: *mut vpkc_update_options_t,
|
p_options: *mut vpkc_update_options_t,
|
||||||
p_locator: *mut vpkc_locator_config_t,
|
p_locator: *mut vpkc_locator_config_t,
|
||||||
p_manager: *mut *mut vpkc_update_manager_t,
|
p_manager: *mut *mut vpkc_update_manager_t,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
wrap_error(|| {
|
wrap_error(|| {
|
||||||
let source = CCallbackUpdateSource {
|
let source = UpdateSourceRawPtr::get_source_clone(p_source).ok_or(anyhow!("pSource must not be null"))?;
|
||||||
p_user_data,
|
|
||||||
cb_get_release_feed,
|
|
||||||
cb_download_release_entry,
|
|
||||||
};
|
|
||||||
let options = c_to_updateoptions_opt(p_options);
|
let options = c_to_updateoptions_opt(p_options);
|
||||||
let locator = c_to_velopacklocatorconfig_opt(p_locator);
|
let locator = c_to_velopacklocatorconfig_opt(p_locator);
|
||||||
let manager = UpdateManager::new(source, options, locator)?;
|
let manager = UpdateManager::new_boxed(source, options, locator)?;
|
||||||
unsafe { *p_manager = UpdateManagerRawPtr::new(manager) };
|
unsafe { *p_manager = UpdateManagerRawPtr::new(manager) };
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
104
src/lib-cpp/src/raw.rs
Normal file
104
src/lib-cpp/src/raw.rs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
use crate::types::*;
|
||||||
|
use velopack::{sources::UpdateSource, UpdateManager};
|
||||||
|
|
||||||
|
pub trait CallbackExt: Sized {
|
||||||
|
fn to_option(self) -> Option<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallbackExt for vpkc_progress_callback_t {
|
||||||
|
fn to_option(self) -> Option<Self> {
|
||||||
|
unsafe { std::mem::transmute::<Self, Option<Self>>(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallbackExt for vpkc_log_callback_t {
|
||||||
|
fn to_option(self) -> Option<Self> {
|
||||||
|
unsafe { std::mem::transmute::<Self, Option<Self>>(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallbackExt for vpkc_hook_callback_t {
|
||||||
|
fn to_option(self) -> Option<Self> {
|
||||||
|
unsafe { std::mem::transmute::<Self, Option<Self>>(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallbackExt for vpkc_release_feed_delegate_t {
|
||||||
|
fn to_option(self) -> Option<Self> {
|
||||||
|
unsafe { std::mem::transmute::<Self, Option<Self>>(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallbackExt for vpkc_download_asset_delegate_t {
|
||||||
|
fn to_option(self) -> Option<Self> {
|
||||||
|
unsafe { std::mem::transmute::<Self, Option<Self>>(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RawPtrExt<'a, T>: Sized {
|
||||||
|
fn to_opaque_ref(self) -> Option<&'a T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RawPtrExt<'a, UpdateManager> for *mut vpkc_update_manager_t {
|
||||||
|
fn to_opaque_ref(self) -> Option<&'a UpdateManager> {
|
||||||
|
if self.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let opaque = unsafe { &*(self as *mut UpdateManager) };
|
||||||
|
Some(opaque)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateManagerRawPtr;
|
||||||
|
|
||||||
|
impl UpdateManagerRawPtr {
|
||||||
|
pub fn new(obj: UpdateManager) -> *mut vpkc_update_manager_t {
|
||||||
|
log::debug!("vpkc_update_manager_t allocated");
|
||||||
|
let boxed = Box::new(obj);
|
||||||
|
Box::into_raw(boxed) as *mut vpkc_update_manager_t
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free(p_manager: *mut vpkc_update_manager_t) {
|
||||||
|
if p_manager.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the raw pointer back into a Box to deallocate it properly
|
||||||
|
log::debug!("vpkc_update_manager_t freed");
|
||||||
|
let _ = unsafe { Box::from_raw(p_manager as *mut UpdateManager) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateSourceContainer {
|
||||||
|
source: Box<dyn UpdateSource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UpdateSourceRawPtr;
|
||||||
|
|
||||||
|
impl UpdateSourceRawPtr {
|
||||||
|
pub fn new(source: Box<dyn UpdateSource>) -> *mut vpkc_update_source_t {
|
||||||
|
log::debug!("vpkc_update_source_t allocated");
|
||||||
|
let boxed = Box::new(UpdateSourceContainer { source });
|
||||||
|
Box::into_raw(boxed) as *mut vpkc_update_source_t
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free(p_source: *mut vpkc_update_source_t) {
|
||||||
|
if p_source.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the raw pointer back into a Box to deallocate it properly
|
||||||
|
log::debug!("vpkc_update_source_t freed");
|
||||||
|
let _ = unsafe { Box::from_raw(p_source as *mut UpdateSourceContainer) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_source_clone(p_source: *mut vpkc_update_source_t) -> Option<Box<dyn UpdateSource>> {
|
||||||
|
if p_source.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let opaque = unsafe { &*(p_source as *mut UpdateSourceContainer) };
|
||||||
|
Some(opaque.source.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,45 @@
|
|||||||
use libc::{c_char, c_void, size_t};
|
use libc::{c_char, c_void, size_t};
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use velopack::{locator::VelopackLocatorConfig, UpdateInfo, UpdateManager, UpdateOptions, VelopackAsset};
|
use velopack::{locator::VelopackLocatorConfig, UpdateInfo, UpdateOptions, VelopackAsset};
|
||||||
|
|
||||||
|
/// The result of a call to check for updates. This can indicate that an update is available, or that an error occurred.
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum vpkc_update_check_t {
|
||||||
|
UPDATE_ERROR = -1,
|
||||||
|
UPDATE_AVAILABLE = 0,
|
||||||
|
NO_UPDATE_AVAILABLE = 1,
|
||||||
|
REMOTE_IS_EMPTY = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opaque type for the Velopack UpdateManager. Must be freed with `vpkc_free_update_manager`.
|
||||||
|
pub type vpkc_update_manager_t = c_void;
|
||||||
|
|
||||||
|
/// Opaque type for a Velopack UpdateSource. Must be freed with `vpkc_free_update_source`.
|
||||||
|
pub type vpkc_update_source_t = c_void;
|
||||||
|
|
||||||
|
/// Progress callback function.
|
||||||
|
pub type vpkc_progress_callback_t = extern "C" fn(p_user_data: *mut c_void, progress: size_t);
|
||||||
|
|
||||||
|
/// Log callback function.
|
||||||
|
pub type vpkc_log_callback_t = extern "C" fn(p_user_data: *mut c_void, psz_level: *const c_char, psz_message: *const c_char);
|
||||||
|
|
||||||
|
/// VelopackApp startup hook callback function.
|
||||||
|
pub type vpkc_hook_callback_t = extern "C" fn(p_user_data: *mut c_void, psz_app_version: *const c_char);
|
||||||
|
|
||||||
|
/// User delegate for to fetch a release feed. This function should return the raw JSON string of the release.json feed.
|
||||||
|
pub type vpkc_release_feed_delegate_t = extern "C" fn(p_user_data: *mut c_void, psz_releases_name: *const c_char) -> *const c_char;
|
||||||
|
|
||||||
|
/// User delegate for downloading an asset file. This function is expected to download the provided asset
|
||||||
|
/// to the provided local file path. Througout, you can use the progress callback to write progress reports.
|
||||||
|
/// The function should return true if the download was successful, false otherwise.
|
||||||
|
/// Progress
|
||||||
|
pub type vpkc_download_asset_delegate_t = extern "C" fn(
|
||||||
|
p_user_data: *mut c_void,
|
||||||
|
p_asset: *const vpkc_asset_t,
|
||||||
|
psz_local_path: *const c_char,
|
||||||
|
progress_callback_id: size_t,
|
||||||
|
) -> bool;
|
||||||
|
|
||||||
pub fn c_to_string_opt(psz: *const c_char) -> Option<String> {
|
pub fn c_to_string_opt(psz: *const c_char) -> Option<String> {
|
||||||
if psz.is_null() {
|
if psz.is_null() {
|
||||||
@@ -86,84 +124,6 @@ pub fn return_cstr(psz: *mut c_char, c: size_t, s: &str) -> size_t {
|
|||||||
return s.len();
|
return s.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of a call to check for updates. This can indicate that an update is available, or that an error occurred.
|
|
||||||
#[repr(i8)]
|
|
||||||
pub enum vpkc_update_check_t {
|
|
||||||
UPDATE_ERROR = -1,
|
|
||||||
UPDATE_AVAILABLE = 0,
|
|
||||||
NO_UPDATE_AVAILABLE = 1,
|
|
||||||
REMOTE_IS_EMPTY = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Opaque type for the Velopack UpdateManager. Must be freed with `vpkc_free_update_manager`.
|
|
||||||
pub type vpkc_update_manager_t = c_void;
|
|
||||||
|
|
||||||
/// Progress callback function.
|
|
||||||
pub type vpkc_progress_callback_t = extern "C" fn(p_user_data: *mut c_void, progress: size_t);
|
|
||||||
|
|
||||||
/// Log callback function.
|
|
||||||
pub type vpkc_log_callback_t = extern "C" fn(p_user_data: *mut c_void, psz_level: *const c_char, psz_message: *const c_char);
|
|
||||||
|
|
||||||
/// VelopackApp startup hook callback function.
|
|
||||||
pub type vpkc_hook_callback_t = extern "C" fn(p_user_data: *mut c_void, psz_app_version: *const c_char);
|
|
||||||
|
|
||||||
pub trait CallbackExt: Sized {
|
|
||||||
fn to_option(self) -> Option<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CallbackExt for vpkc_progress_callback_t {
|
|
||||||
fn to_option(self) -> Option<Self> {
|
|
||||||
unsafe { std::mem::transmute::<Self, Option<Self>>(self) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CallbackExt for vpkc_log_callback_t {
|
|
||||||
fn to_option(self) -> Option<Self> {
|
|
||||||
unsafe { std::mem::transmute::<Self, Option<Self>>(self) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CallbackExt for vpkc_hook_callback_t {
|
|
||||||
fn to_option(self) -> Option<Self> {
|
|
||||||
unsafe { std::mem::transmute::<Self, Option<Self>>(self) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait UpdateManagerExt<'a>: Sized {
|
|
||||||
fn to_opaque_ref(self) -> Option<&'a UpdateManager>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> UpdateManagerExt<'a> for *mut vpkc_update_manager_t {
|
|
||||||
fn to_opaque_ref(self) -> Option<&'a UpdateManager> {
|
|
||||||
if self.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let opaque = unsafe { &*(self as *mut UpdateManager) };
|
|
||||||
Some(opaque)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UpdateManagerRawPtr;
|
|
||||||
|
|
||||||
impl UpdateManagerRawPtr {
|
|
||||||
pub fn new(obj: UpdateManager) -> *mut vpkc_update_manager_t {
|
|
||||||
log::debug!("vpkc_update_manager_t allocated");
|
|
||||||
let boxed = Box::new(obj);
|
|
||||||
Box::into_raw(boxed) as *mut vpkc_update_manager_t
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn free(p_manager: *mut vpkc_update_manager_t) {
|
|
||||||
if p_manager.is_null() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the raw pointer back into a Box to deallocate it properly
|
|
||||||
log::debug!("vpkc_update_manager_t freed");
|
|
||||||
let _ = unsafe { Box::from_raw(p_manager as *mut UpdateManager) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// !! AUTO-GENERATED-START RUST_TYPES
|
// !! AUTO-GENERATED-START RUST_TYPES
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|||||||
@@ -141,6 +141,23 @@ impl UpdateManager {
|
|||||||
source: T,
|
source: T,
|
||||||
options: Option<UpdateOptions>,
|
options: Option<UpdateOptions>,
|
||||||
locator: Option<VelopackLocatorConfig>,
|
locator: Option<VelopackLocatorConfig>,
|
||||||
|
) -> Result<UpdateManager, Error> {
|
||||||
|
UpdateManager::new_boxed(source.clone_boxed(), options, locator)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new UpdateManager instance using the specified UpdateSource.
|
||||||
|
/// This will return an error if the application is not yet installed.
|
||||||
|
/// ## Example:
|
||||||
|
/// ```rust
|
||||||
|
/// use velopack::*;
|
||||||
|
///
|
||||||
|
/// let source = sources::HttpSource::new("https://the.place/you-host/updates");
|
||||||
|
/// let um = UpdateManager::new_boxed(Box::new(source), None, None);
|
||||||
|
/// ```
|
||||||
|
pub fn new_boxed(
|
||||||
|
source: Box<dyn UpdateSource>,
|
||||||
|
options: Option<UpdateOptions>,
|
||||||
|
locator: Option<VelopackLocatorConfig>,
|
||||||
) -> Result<UpdateManager, Error> {
|
) -> Result<UpdateManager, Error> {
|
||||||
let locator = if let Some(config) = locator {
|
let locator = if let Some(config) = locator {
|
||||||
warn!("Using explicit locator configuration, ignoring auto-locate.");
|
warn!("Using explicit locator configuration, ignoring auto-locate.");
|
||||||
@@ -151,7 +168,7 @@ impl UpdateManager {
|
|||||||
};
|
};
|
||||||
Ok(UpdateManager {
|
Ok(UpdateManager {
|
||||||
options: options.unwrap_or_default(),
|
options: options.unwrap_or_default(),
|
||||||
source: source.clone_boxed(),
|
source,
|
||||||
locator,
|
locator,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user