diff --git a/src/lib-cpp/include/Velopack.hpp b/src/lib-cpp/include/Velopack.hpp index 0cc7950a..64460c95 100644 --- a/src/lib-cpp/include/Velopack.hpp +++ b/src/lib-cpp/include/Velopack.hpp @@ -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 +inline T unwrap(const std::optional& 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 to_cpp_VelopackLocatorConfig(const vpkc_locator_config_t* dto) { if (dto == nullptr) { return std::nullopt; } return std::optional({ - 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 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& 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& dto, size_t* count) { @@ -194,15 +202,15 @@ struct VelopackAsset { static inline std::optional to_cpp_VelopackAsset(const vpkc_asset_t* dto) { if (dto == nullptr) { return std::nullopt; } return std::optional({ - 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 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& 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& dto, size_t* count) { @@ -292,7 +301,7 @@ struct UpdateInfo { static inline std::optional to_cpp_UpdateInfo(const vpkc_update_info_t* dto) { if (dto == nullptr) { return std::nullopt; } return std::optional({ - 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 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& 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& dto, size_t* count) { @@ -398,12 +408,12 @@ static inline std::vector 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& 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& 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 >> UpdateManager(std::unique_ptr 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(static_cast(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; + } }; /** diff --git a/src/lib-cpp/src/types.rs b/src/lib-cpp/src/types.rs index 1cbcc13f..48d45c9e 100644 --- a/src/lib-cpp/src/types.rs +++ b/src/lib-cpp/src/types.rs @@ -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, 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, 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, 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]); diff --git a/src/lib-cpp/type-generator/Templates/cpp_mapping.hbs b/src/lib-cpp/type-generator/Templates/cpp_mapping.hbs index 46401a1d..64d56525 100644 --- a/src/lib-cpp/type-generator/Templates/cpp_mapping.hbs +++ b/src/lib-cpp/type-generator/Templates/cpp_mapping.hbs @@ -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) { diff --git a/src/lib-cpp/type-generator/Templates/rust_types.hbs b/src/lib-cpp/type-generator/Templates/rust_types.hbs index 6a01bcd4..daa9d393 100644 --- a/src/lib-cpp/type-generator/Templates/rust_types.hbs +++ b/src/lib-cpp/type-generator/Templates/rust_types.hbs @@ -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]); diff --git a/src/lib-rust/src/manager.rs b/src/lib-rust/src/manager.rs index b90a4abe..fd6233e9 100644 --- a/src/lib-rust/src/manager.rs +++ b/src/lib-rust/src/manager.rs @@ -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) { - 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>, ) -> 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 = ["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 = - ["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() {