mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Add support for custom update sources in the C++ library
This commit is contained in:
committed by
Caelan
parent
8ac4f68467
commit
304eac3b71
@@ -23,6 +23,7 @@ anyhow.workspace = true
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
libc.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen.workspace = true
|
||||
@@ -175,6 +175,30 @@ bool vpkc_new_update_manager(const char *psz_url_or_path,
|
||||
struct vpkc_locator_config_t *p_locator,
|
||||
vpkc_update_manager_t **p_manager);
|
||||
|
||||
/**
|
||||
* Create a new UpdateManager instance.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Returns the currently installed version of the app.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#include "Velopack.h"
|
||||
|
||||
@@ -333,12 +334,31 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Implement this interface override the default update source
|
||||
* (AutoSource)
|
||||
*/
|
||||
class IUpdateSource {
|
||||
public:
|
||||
/** @param Retrieve the list of available remote releases from
|
||||
* the package source. These releases can subsequently be downloaded
|
||||
* with DownloadReleaseEntry().
|
||||
*/
|
||||
virtual const char* GetReleaseFeed(const char* releasesName) = 0;
|
||||
|
||||
/** @param Download the specified VelopackAsset to the provided local file path.
|
||||
*/
|
||||
virtual bool DownloadReleaseEntry(const char* assetFileName, const char* localFilePath) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides functionality for checking for updates, downloading updates, and applying updates to the current application.
|
||||
*/
|
||||
class UpdateManager {
|
||||
private:
|
||||
vpkc_update_manager_t* m_pManager = 0;
|
||||
std::unique_ptr<IUpdateSource> m_pUpdateSource;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new UpdateManager instance.
|
||||
@@ -366,6 +386,42 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new UpdateManager instance.
|
||||
* @param pUpdateSource Custom update source implementation.
|
||||
* @param options Optional extra configuration for update manager.
|
||||
* @param locator Override the default locator configuration (usually used for testing / mocks).
|
||||
*/
|
||||
UpdateManager(std::unique_ptr<IUpdateSource> pUpdateSource, const UpdateOptions* options = nullptr, const VelopackLocatorConfig* locator = nullptr) {
|
||||
vpkc_update_options_t* pOptions = nullptr;
|
||||
if (options != nullptr) {
|
||||
vpkc_update_options_t vpkc_options = to_c(*options);
|
||||
pOptions = &vpkc_options;
|
||||
}
|
||||
|
||||
vpkc_locator_config_t* pLocator = nullptr;
|
||||
if (locator != nullptr) {
|
||||
vpkc_locator_config_t vpkc_locator = to_c(*locator);
|
||||
pLocator = &vpkc_locator;
|
||||
}
|
||||
|
||||
auto cbGetReleaseFeed = [](const char* releasesName, void* userData) {
|
||||
IUpdateSource* source = reinterpret_cast<IUpdateSource*>(userData);
|
||||
return source->GetReleaseFeed(releasesName);
|
||||
};
|
||||
|
||||
auto cbDownloadReleaseEntry = [](const char* assetFileName, const char* localFilePath, void* userData) {
|
||||
IUpdateSource* source = reinterpret_cast<IUpdateSource*>(userData);
|
||||
return source->DownloadReleaseEntry(assetFileName, localFilePath);
|
||||
};
|
||||
|
||||
m_pUpdateSource.swap(pUpdateSource);
|
||||
|
||||
if (!vpkc_new_custom_update_manager(cbGetReleaseFeed, cbDownloadReleaseEntry, m_pUpdateSource.get(), pOptions, pLocator, &m_pManager)) {
|
||||
throw_last_error();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Destructor for UpdateManager.
|
||||
*/
|
||||
@@ -480,4 +536,4 @@ public:
|
||||
|
||||
} // namespace Velopack
|
||||
|
||||
#endif // VELOPACK_HPP
|
||||
#endif // VELOPACK_HPP
|
||||
|
||||
@@ -10,8 +10,8 @@ use types::*;
|
||||
|
||||
use anyhow::{anyhow, bail};
|
||||
use libc::{c_char, c_void, size_t};
|
||||
use std::ffi::CString;
|
||||
use velopack::{sources, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp};
|
||||
use std::{sync::mpsc::Sender, ffi::{CStr, CString}};
|
||||
use velopack::{bundle, sources, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp, VelopackAsset, VelopackAssetFeed};
|
||||
|
||||
/// Create a new UpdateManager instance.
|
||||
/// @param urlOrPath Location of the update server or path to the local update directory.
|
||||
@@ -35,6 +35,95 @@ 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.
|
||||
/// @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,
|
||||
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 options = c_to_updateoptions_opt(p_options);
|
||||
let locator = c_to_velopacklocatorconfig_opt(p_locator);
|
||||
let manager = UpdateManager::new(source, options, locator)?;
|
||||
unsafe { *p_manager = UpdateManagerRawPtr::new(manager) };
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the currently installed version of the app.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn vpkc_get_current_version(p_manager: *mut vpkc_update_manager_t, psz_version: *mut c_char, c_version: size_t) -> size_t {
|
||||
|
||||
Reference in New Issue
Block a user