Fix various bugs with rust delta process

This commit is contained in:
Caelan Sayler
2025-05-23 18:30:44 +01:00
committed by Caelan
parent a7fabcd237
commit 00ba467373
5 changed files with 75 additions and 50 deletions

View File

@@ -1,6 +1,5 @@
//! This header provides the C++ API for the Velopack library.
//! This C++ API is a thin wrapper around the C API, providing a more idiomatic C++ interface.
//! You should not mix and match the C and C++ APIs in the same program.
#ifndef VELOPACK_HPP
#define VELOPACK_HPP
@@ -78,6 +77,14 @@ static inline void free_c_string_vec(char** arr, size_t size)
delete[] arr;
}
template<typename T>
inline T unwrap(const std::optional<T>& opt, const std::string& message = "Expected value not present") {
if (!opt.has_value()) {
throw std::runtime_error(message);
}
return opt.value();
}
// !! AUTO-GENERATED-START CPP_TYPES
/** VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). */
@@ -99,11 +106,11 @@ struct VelopackLocatorConfig {
static inline std::optional<VelopackLocatorConfig> to_cpp_VelopackLocatorConfig(const vpkc_locator_config_t* dto) {
if (dto == nullptr) { return std::nullopt; }
return std::optional<VelopackLocatorConfig>({
to_cpp_string(dto->RootAppDir).value(),
to_cpp_string(dto->UpdateExePath).value(),
to_cpp_string(dto->PackagesDir).value(),
to_cpp_string(dto->ManifestPath).value(),
to_cpp_string(dto->CurrentBinaryDir).value(),
unwrap(to_cpp_string(dto->RootAppDir), "Required property RootAppDir was null"),
unwrap(to_cpp_string(dto->UpdateExePath), "Required property UpdateExePath was null"),
unwrap(to_cpp_string(dto->PackagesDir), "Required property PackagesDir was null"),
unwrap(to_cpp_string(dto->ManifestPath), "Required property ManifestPath was null"),
unwrap(to_cpp_string(dto->CurrentBinaryDir), "Required property CurrentBinaryDir was null"),
dto->IsPortable,
});
}
@@ -115,12 +122,12 @@ static inline std::vector<VelopackLocatorConfig> to_cpp_VelopackLocatorConfig_ve
for (size_t i = 0; i < c; ++i) {
auto dto = arr[i];
if (dto == nullptr) { continue; }
result.push_back(to_cpp_VelopackLocatorConfig(dto).value());
result.push_back(unwrap(to_cpp_VelopackLocatorConfig(dto)));
}
return result;
}
static inline vpkc_locator_config_t* alloc_c_VelopackLocatorConfig(const VelopackLocatorConfig* dto) {
static inline vpkc_locator_config_t* alloc_c_VelopackLocatorConfig_ptr(const VelopackLocatorConfig* dto) {
if (dto == nullptr) { return nullptr; }
vpkc_locator_config_t* obj = new vpkc_locator_config_t{};
obj->RootAppDir = alloc_c_string(dto->RootAppDir);
@@ -134,7 +141,8 @@ static inline vpkc_locator_config_t* alloc_c_VelopackLocatorConfig(const Velopac
static inline vpkc_locator_config_t* alloc_c_VelopackLocatorConfig(const std::optional<VelopackLocatorConfig>& dto) {
if (!dto.has_value()) { return nullptr; }
return alloc_c_VelopackLocatorConfig(dto.value());
VelopackLocatorConfig obj = unwrap(dto);
return alloc_c_VelopackLocatorConfig_ptr(&obj);
}
static inline vpkc_locator_config_t** alloc_c_VelopackLocatorConfig_vec(const std::vector<VelopackLocatorConfig>& dto, size_t* count) {
@@ -194,15 +202,15 @@ struct VelopackAsset {
static inline std::optional<VelopackAsset> to_cpp_VelopackAsset(const vpkc_asset_t* dto) {
if (dto == nullptr) { return std::nullopt; }
return std::optional<VelopackAsset>({
to_cpp_string(dto->PackageId).value(),
to_cpp_string(dto->Version).value(),
to_cpp_string(dto->Type).value(),
to_cpp_string(dto->FileName).value(),
to_cpp_string(dto->SHA1).value(),
to_cpp_string(dto->SHA256).value(),
unwrap(to_cpp_string(dto->PackageId), "Required property PackageId was null"),
unwrap(to_cpp_string(dto->Version), "Required property Version was null"),
unwrap(to_cpp_string(dto->Type), "Required property Type was null"),
unwrap(to_cpp_string(dto->FileName), "Required property FileName was null"),
unwrap(to_cpp_string(dto->SHA1), "Required property SHA1 was null"),
unwrap(to_cpp_string(dto->SHA256), "Required property SHA256 was null"),
dto->Size,
to_cpp_string(dto->NotesMarkdown).value(),
to_cpp_string(dto->NotesHtml).value(),
unwrap(to_cpp_string(dto->NotesMarkdown), "Required property NotesMarkdown was null"),
unwrap(to_cpp_string(dto->NotesHtml), "Required property NotesHtml was null"),
});
}
@@ -213,12 +221,12 @@ static inline std::vector<VelopackAsset> to_cpp_VelopackAsset_vec(const vpkc_ass
for (size_t i = 0; i < c; ++i) {
auto dto = arr[i];
if (dto == nullptr) { continue; }
result.push_back(to_cpp_VelopackAsset(dto).value());
result.push_back(unwrap(to_cpp_VelopackAsset(dto)));
}
return result;
}
static inline vpkc_asset_t* alloc_c_VelopackAsset(const VelopackAsset* dto) {
static inline vpkc_asset_t* alloc_c_VelopackAsset_ptr(const VelopackAsset* dto) {
if (dto == nullptr) { return nullptr; }
vpkc_asset_t* obj = new vpkc_asset_t{};
obj->PackageId = alloc_c_string(dto->PackageId);
@@ -235,7 +243,8 @@ static inline vpkc_asset_t* alloc_c_VelopackAsset(const VelopackAsset* dto) {
static inline vpkc_asset_t* alloc_c_VelopackAsset(const std::optional<VelopackAsset>& dto) {
if (!dto.has_value()) { return nullptr; }
return alloc_c_VelopackAsset(dto.value());
VelopackAsset obj = unwrap(dto);
return alloc_c_VelopackAsset_ptr(&obj);
}
static inline vpkc_asset_t** alloc_c_VelopackAsset_vec(const std::vector<VelopackAsset>& dto, size_t* count) {
@@ -292,7 +301,7 @@ struct UpdateInfo {
static inline std::optional<UpdateInfo> to_cpp_UpdateInfo(const vpkc_update_info_t* dto) {
if (dto == nullptr) { return std::nullopt; }
return std::optional<UpdateInfo>({
to_cpp_VelopackAsset(dto->TargetFullRelease).value(),
unwrap(to_cpp_VelopackAsset(dto->TargetFullRelease), "Required property TargetFullRelease was null"),
to_cpp_VelopackAsset(dto->BaseRelease),
to_cpp_VelopackAsset_vec(dto->DeltasToTarget, dto->DeltasToTargetCount),
dto->IsDowngrade,
@@ -306,12 +315,12 @@ static inline std::vector<UpdateInfo> to_cpp_UpdateInfo_vec(const vpkc_update_in
for (size_t i = 0; i < c; ++i) {
auto dto = arr[i];
if (dto == nullptr) { continue; }
result.push_back(to_cpp_UpdateInfo(dto).value());
result.push_back(unwrap(to_cpp_UpdateInfo(dto)));
}
return result;
}
static inline vpkc_update_info_t* alloc_c_UpdateInfo(const UpdateInfo* dto) {
static inline vpkc_update_info_t* alloc_c_UpdateInfo_ptr(const UpdateInfo* dto) {
if (dto == nullptr) { return nullptr; }
vpkc_update_info_t* obj = new vpkc_update_info_t{};
obj->TargetFullRelease = alloc_c_VelopackAsset(dto->TargetFullRelease);
@@ -323,7 +332,8 @@ static inline vpkc_update_info_t* alloc_c_UpdateInfo(const UpdateInfo* dto) {
static inline vpkc_update_info_t* alloc_c_UpdateInfo(const std::optional<UpdateInfo>& dto) {
if (!dto.has_value()) { return nullptr; }
return alloc_c_UpdateInfo(dto.value());
UpdateInfo obj = unwrap(dto);
return alloc_c_UpdateInfo_ptr(&obj);
}
static inline vpkc_update_info_t** alloc_c_UpdateInfo_vec(const std::vector<UpdateInfo>& dto, size_t* count) {
@@ -398,12 +408,12 @@ static inline std::vector<UpdateOptions> to_cpp_UpdateOptions_vec(const vpkc_upd
for (size_t i = 0; i < c; ++i) {
auto dto = arr[i];
if (dto == nullptr) { continue; }
result.push_back(to_cpp_UpdateOptions(dto).value());
result.push_back(unwrap(to_cpp_UpdateOptions(dto)));
}
return result;
}
static inline vpkc_update_options_t* alloc_c_UpdateOptions(const UpdateOptions* dto) {
static inline vpkc_update_options_t* alloc_c_UpdateOptions_ptr(const UpdateOptions* dto) {
if (dto == nullptr) { return nullptr; }
vpkc_update_options_t* obj = new vpkc_update_options_t{};
obj->AllowVersionDowngrade = dto->AllowVersionDowngrade;
@@ -414,7 +424,8 @@ static inline vpkc_update_options_t* alloc_c_UpdateOptions(const UpdateOptions*
static inline vpkc_update_options_t* alloc_c_UpdateOptions(const std::optional<UpdateOptions>& dto) {
if (!dto.has_value()) { return nullptr; }
return alloc_c_UpdateOptions(dto.value());
UpdateOptions obj = unwrap(dto);
return alloc_c_UpdateOptions_ptr(&obj);
}
static inline vpkc_update_options_t** alloc_c_UpdateOptions_vec(const std::vector<UpdateOptions>& dto, size_t* count) {
@@ -669,8 +680,8 @@ public:
* @param locator Override the default locator configuration (usually used for testing / mocks).
*/
UpdateManager(const std::string& urlOrPath, const UpdateOptions* options = nullptr, const VelopackLocatorConfig* locator = nullptr) {
vpkc_update_options_t* pOptions = alloc_c_UpdateOptions(options);
vpkc_locator_config_t* pLocator = alloc_c_VelopackLocatorConfig(locator);
vpkc_update_options_t* pOptions = alloc_c_UpdateOptions_ptr(options);
vpkc_locator_config_t* pLocator = alloc_c_VelopackLocatorConfig_ptr(locator);
bool result = vpkc_new_update_manager(urlOrPath.c_str(), pOptions, pLocator, &m_pManager);
free_c_UpdateOptions(pOptions);
free_c_VelopackLocatorConfig(pLocator);
@@ -687,8 +698,8 @@ public:
*/
template <typename T, typename = std::enable_if_t<std::is_base_of_v<IUpdateSource, T>>>
UpdateManager(std::unique_ptr<T> pUpdateSource, const UpdateOptions* options = nullptr, const VelopackLocatorConfig* locator = nullptr) {
vpkc_update_options_t* pOptions = alloc_c_UpdateOptions(options);
vpkc_locator_config_t* pLocator = alloc_c_VelopackLocatorConfig(locator);
vpkc_update_options_t* pOptions = alloc_c_UpdateOptions_ptr(options);
vpkc_locator_config_t* pLocator = alloc_c_VelopackLocatorConfig_ptr(locator);
m_pUpdateSource = std::unique_ptr<IUpdateSource>(static_cast<IUpdateSource*>(pUpdateSource.release()));
vpkc_update_source_t* pSource = m_pUpdateSource->m_pSource;
bool result = vpkc_new_update_manager_with_source(pSource, pOptions, pLocator, &m_pManager);
@@ -703,7 +714,10 @@ public:
* Destructor for UpdateManager.
*/
~UpdateManager() {
vpkc_free_update_manager(m_pManager);
if (m_pManager != nullptr) {
vpkc_free_update_manager(m_pManager);
m_pManager = nullptr;
}
};
/**

View File

@@ -2,6 +2,7 @@ use anyhow::{bail, Result};
use libc::{c_char, c_void, size_t};
use std::ffi::{CStr, CString};
use std::path::PathBuf;
use std::mem::size_of;
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.
@@ -89,9 +90,10 @@ pub fn allocate_PathBuf(p: &PathBuf) -> *mut c_char {
pub unsafe fn free_String(psz: *mut c_char) {
if !psz.is_null() {
let _ = unsafe { CString::from_raw(psz) };
drop(CString::from_raw(psz));
}
}
pub unsafe fn free_PathBuf(psz: *mut c_char) {
free_String(psz);
}
@@ -181,6 +183,7 @@ pub unsafe fn allocate_VelopackLocatorConfig_vec(dto: &Vec<VelopackLocatorConfig
}
log::debug!("vpkc_locator_config_t vector allocated");
let count_value = dto.len() as size_t;
*count = count_value;
let mut assets = Vec::with_capacity(count_value as usize);
for i in 0..count_value {
let ptr = allocate_VelopackLocatorConfig(&dto[i as usize]);
@@ -297,6 +300,7 @@ pub unsafe fn allocate_VelopackAsset_vec(dto: &Vec<VelopackAsset>, count: *mut s
}
log::debug!("vpkc_asset_t vector allocated");
let count_value = dto.len() as size_t;
*count = count_value;
let mut assets = Vec::with_capacity(count_value as usize);
for i in 0..count_value {
let ptr = allocate_VelopackAsset(&dto[i as usize]);
@@ -400,6 +404,7 @@ pub unsafe fn allocate_UpdateInfo_vec(dto: &Vec<UpdateInfo>, count: *mut size_t)
}
log::debug!("vpkc_update_info_t vector allocated");
let count_value = dto.len() as size_t;
*count = count_value;
let mut assets = Vec::with_capacity(count_value as usize);
for i in 0..count_value {
let ptr = allocate_UpdateInfo(&dto[i as usize]);
@@ -500,6 +505,7 @@ pub unsafe fn allocate_UpdateOptions_vec(dto: &Vec<UpdateOptions>, count: *mut s
}
log::debug!("vpkc_update_options_t vector allocated");
let count_value = dto.len() as size_t;
*count = count_value;
let mut assets = Vec::with_capacity(count_value as usize);
for i in 0..count_value {
let ptr = allocate_UpdateOptions(&dto[i as usize]);

View File

@@ -16,7 +16,7 @@ static inline std::optional<{{struct_rust_name}}> to_cpp_{{struct_rust_name}}(co
return std::optional<{{struct_rust_name}}>({
{{#each fields}}
{{#if field_primitive}}dto->{{field_name}},{{/if~}}
{{#if field_normal}}to_cpp_{{field_cpp_type}}(dto->{{field_name}}){{~#unless field_optional}}.value(){{/unless}},{{/if~}}
{{#if field_normal}}{{#unless field_optional}}unwrap({{/unless}}to_cpp_{{field_cpp_type}}(dto->{{field_name}}){{~#unless field_optional}}, "Required property {{field_name}} was null"){{/unless}},{{/if~}}
{{#if field_vector}}to_cpp_{{field_cpp_type}}_vec(dto->{{field_name}}, dto->{{field_name}}Count),{{/if}}
{{/each}}
});
@@ -29,12 +29,12 @@ static inline std::vector<{{struct_rust_name}}> to_cpp_{{struct_rust_name}}_vec(
for (size_t i = 0; i < c; ++i) {
auto dto = arr[i];
if (dto == nullptr) { continue; }
result.push_back(to_cpp_{{struct_rust_name}}(dto).value());
result.push_back(unwrap(to_cpp_{{struct_rust_name}}(dto)));
}
return result;
}
static inline {{struct_c_name}}* alloc_c_{{struct_rust_name}}(const {{struct_rust_name}}* dto) {
static inline {{struct_c_name}}* alloc_c_{{struct_rust_name}}_ptr(const {{struct_rust_name}}* dto) {
if (dto == nullptr) { return nullptr; }
{{struct_c_name}}* obj = new {{struct_c_name}}{};
{{#each fields}}
@@ -47,7 +47,8 @@ static inline {{struct_c_name}}* alloc_c_{{struct_rust_name}}(const {{struct_rus
static inline {{struct_c_name}}* alloc_c_{{struct_rust_name}}(const std::optional<{{struct_rust_name}}>& dto) {
if (!dto.has_value()) { return nullptr; }
return alloc_c_{{struct_rust_name}}(dto.value());
{{struct_rust_name}} obj = unwrap(dto);
return alloc_c_{{struct_rust_name}}_ptr(&obj);
}
static inline {{struct_c_name}}** alloc_c_{{struct_rust_name}}_vec(const std::vector<{{struct_rust_name}}>& dto, size_t* count) {

View File

@@ -63,6 +63,7 @@ pub unsafe fn allocate_{{struct_rust_name}}_vec(dto: &Vec<{{struct_rust_name}}>,
}
log::debug!("{{struct_c_name}} vector allocated");
let count_value = dto.len() as size_t;
*count = count_value;
let mut assets = Vec::with_capacity(count_value as usize);
for i in 0..count_value {
let ptr = allocate_{{struct_rust_name}}(&dto[i as usize]);

View File

@@ -398,7 +398,7 @@ impl UpdateManager {
fs::create_dir_all(packages_dir)?;
let final_target_file = packages_dir.join(name);
let partial_file = final_target_file.with_extension(".partial");
let partial_file = final_target_file.with_extension("partial");
if final_target_file.exists() {
info!("Package already exists on disk, skipping download: '{}'", final_target_file.to_string_lossy());
@@ -407,11 +407,10 @@ impl UpdateManager {
let old_nupkg_pattern = format!("{}/*.nupkg", packages_dir.to_string_lossy());
let old_partial_pattern = format!("{}/*.partial", packages_dir.to_string_lossy());
let delta_pattern = format!("{}/-delta.nupkg", packages_dir.to_string_lossy());
let delta_pattern = format!("{}/*-delta.nupkg", packages_dir.to_string_lossy());
let mut to_delete = Vec::new();
fn find_files_to_delete(pattern: &str, to_delete: &mut Vec<String>) {
info!("Searching for packages to clean: '{}'", pattern);
match glob::glob(pattern) {
Ok(paths) => {
for path in paths.into_iter().flatten() {
@@ -434,14 +433,14 @@ impl UpdateManager {
info!("Falling back to full update...");
self.source.download_release_entry(&update.TargetFullRelease, &partial_file.to_string_lossy(), progress)?;
self.verify_package_checksum(&partial_file, &update.TargetFullRelease)?;
info!("Successfully downloaded file: '{}'", partial_file.to_string_lossy());
}
} else {
self.source.download_release_entry(&update.TargetFullRelease, &partial_file.to_string_lossy(), progress)?;
self.verify_package_checksum(&partial_file, &update.TargetFullRelease)?;
info!("Successfully downloaded file: '{}'", partial_file.to_string_lossy());
}
info!("Successfully downloaded file: '{}'", partial_file.to_string_lossy());
info!("Renaming partial file to final target: '{}'", final_target_file.to_string_lossy());
fs::rename(&partial_file, &final_target_file)?;
@@ -477,6 +476,12 @@ impl UpdateManager {
progress: Option<Sender<i16>>,
) -> Result<(), Error> {
let packages_dir = self.locator.get_packages_dir();
let base_release_path = packages_dir.join(&update.BaseRelease.as_ref().unwrap().FileName);
let base_release_path = base_release_path.to_string_lossy().to_string();
let output_path = target_file.to_string_lossy().to_string();
let mut args: Vec<String> = ["patch", "--old", &base_release_path, "--output", &output_path].iter().map(|s| s.to_string()).collect();
for (i, delta) in update.DeltasToTarget.iter().enumerate() {
let delta_file = packages_dir.join(&delta.FileName);
let partial_file = delta_file.with_extension("partial");
@@ -490,18 +495,16 @@ impl UpdateManager {
if let Some(progress) = &progress {
let _ = progress.send(((i as f64 / update.DeltasToTarget.len() as f64) * 70.0) as i16);
}
}
let mut args: Vec<String> =
["patch", "--old", &update.BaseRelease.as_ref().unwrap().FileName, "--output"].iter().map(|s| s.to_string()).collect();
args.push(target_file.to_string_lossy().to_string());
for delta in update.DeltasToTarget.iter() {
args.push("--delta".to_string());
let path = packages_dir.join(&delta.FileName);
args.push(path.to_string_lossy().to_string());
args.push(delta_file.to_string_lossy().to_string());
}
info!("Applying {} patches to {}.", update.DeltasToTarget.len(), target_file.to_string_lossy());
info!("Applying {} patches to {}.", update.DeltasToTarget.len(), output_path);
if let Some(progress) = &progress {
let _ = progress.send(70);
}
let output = std::process::Command::new(self.locator.get_update_path()).args(args).output()?;
if output.status.success() {