Implement new lib-c sources API

This commit is contained in:
Caelan Sayler
2024-12-19 16:37:25 +00:00
committed by Caelan
parent 304eac3b71
commit db543994ba
6 changed files with 410 additions and 218 deletions

View File

@@ -27,6 +27,69 @@ enum vpkc_update_check_t
typedef int8_t vpkc_update_check_t;
#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.
*/
@@ -85,48 +148,6 @@ typedef struct vpkc_locator_config_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.
*/
@@ -164,6 +185,38 @@ typedef void (*vpkc_log_callback_t)(void *p_user_data,
extern "C" {
#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.
* @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);
/**
* 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 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 (*cb_download_release_entry)(const char*,
const char*,
void*),
void *p_user_data,
struct vpkc_update_options_t *p_options,
struct vpkc_locator_config_t *p_locator,
vpkc_update_manager_t **p_manager);
bool vpkc_new_update_manager_with_source(vpkc_update_source_t *p_source,
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.

View 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())
}
}

View File

@@ -4,14 +4,76 @@
mod statics;
use statics::*;
mod types;
use types::*;
mod csource;
use csource::*;
mod raw;
use raw::*;
use anyhow::{anyhow, bail};
use libc::{c_char, c_void, size_t};
use std::{sync::mpsc::Sender, ffi::{CStr, CString}};
use velopack::{bundle, sources, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp, VelopackAsset, VelopackAssetFeed};
use std::{ffi::CString, ptr};
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.
/// @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)]
/// Retrieves available updates using a custom method. This is
/// 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.
/// 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 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]
pub extern "C" fn vpkc_new_custom_update_manager(
cb_get_release_feed: extern "C" fn(*const c_char, *mut c_void) -> *const c_char,
cb_download_release_entry: extern "C" fn(*const c_char, *const c_char, *mut c_void) -> bool,
p_user_data: *mut c_void,
pub extern "C" fn vpkc_new_update_manager_with_source(
p_source: *mut vpkc_update_source_t,
p_options: *mut vpkc_update_options_t,
p_locator: *mut vpkc_locator_config_t,
p_manager: *mut *mut vpkc_update_manager_t,
) -> bool {
wrap_error(|| {
let source = CCallbackUpdateSource {
p_user_data,
cb_get_release_feed,
cb_download_release_entry,
};
let source = UpdateSourceRawPtr::get_source_clone(p_source).ok_or(anyhow!("pSource must not be null"))?;
let options = c_to_updateoptions_opt(p_options);
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) };
Ok(())
})

104
src/lib-cpp/src/raw.rs Normal file
View 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())
}
}

View File

@@ -1,7 +1,45 @@
use libc::{c_char, c_void, size_t};
use std::ffi::{CStr, CString};
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> {
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();
}
/// 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
#[rustfmt::skip]
#[repr(C)]

View File

@@ -141,6 +141,23 @@ impl UpdateManager {
source: T,
options: Option<UpdateOptions>,
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> {
let locator = if let Some(config) = locator {
warn!("Using explicit locator configuration, ignoring auto-locate.");
@@ -151,7 +168,7 @@ impl UpdateManager {
};
Ok(UpdateManager {
options: options.unwrap_or_default(),
source: source.clone_boxed(),
source,
locator,
})
}