Add new type mapping generator

This commit is contained in:
Caelan Sayler
2024-10-19 22:01:58 +01:00
committed by Caelan
parent 704ea436c9
commit b09c59649d
12 changed files with 1519 additions and 525 deletions

View File

@@ -53,17 +53,13 @@ typedef void (*vpkc_progress_callback_t)(size_t progress);
typedef void (*vpkc_log_callback_t)(const char* pszLevel, const char* pszMessage);
typedef void (*vpkc_hook_callback_t)(const char* pszAppVersion);
typedef struct {
bool AllowVersionDowngrade;
char* ExplicitChannel;
} vpkc_options_t;
typedef enum {
UPDATE_AVAILABLE = 0,
NO_UPDATE_AVAILABLE = 1,
ERROR = 2,
} vpkc_update_check_t;
// !! AUTO-GENERATED-START C_TYPES
typedef struct {
char* RootAppDir;
char* UpdateExePath;
@@ -71,7 +67,7 @@ typedef struct {
char* ManifestPath;
char* CurrentBinaryDir;
bool IsPortable;
} vpkc_locator_t;
} vpkc_locator_config_t;
typedef struct {
char* PackageId;
@@ -90,8 +86,14 @@ typedef struct {
bool IsDowngrade;
} vpkc_update_info_t;
typedef struct {
bool AllowVersionDowngrade;
char* ExplicitChannel;
} vpkc_update_options_t;
// !! AUTO-GENERATED-END C_TYPES
// Update Manager
VPKC_EXPORT bool VPKC_CALL vpkc_new_update_manager(const char* pszUrlOrString, const vpkc_options_t* pOptions, vpkc_locator_t* pLocator, vpkc_update_manager_t* pManager);
VPKC_EXPORT bool VPKC_CALL vpkc_new_update_manager(const char* pszUrlOrString, vpkc_update_options_t* pOptions, vpkc_locator_config_t* pLocator, vpkc_update_manager_t* pManager);
VPKC_EXPORT size_t VPKC_CALL vpkc_get_current_version(vpkc_update_manager_t* pManager, char* pszVersion, size_t cVersion);
VPKC_EXPORT size_t VPKC_CALL vpkc_get_app_id(vpkc_update_manager_t* pManager, char* pszId, size_t cId);
VPKC_EXPORT bool VPKC_CALL vpkc_is_portable(vpkc_update_manager_t* pManager);
@@ -103,7 +105,7 @@ VPKC_EXPORT bool VPKC_CALL vpkc_wait_exit_then_apply_update(vpkc_update_manager_
// VelopackApp
VPKC_EXPORT void VPKC_CALL vpkc_app_set_auto_apply_on_startup(bool bAutoApply);
VPKC_EXPORT void VPKC_CALL vpkc_app_set_args(char** pArgs, size_t cArgs);
VPKC_EXPORT void VPKC_CALL vpkc_app_set_locator(vpkc_locator_t* pLocator);
VPKC_EXPORT void VPKC_CALL vpkc_app_set_locator(vpkc_locator_config_t* pLocator);
VPKC_EXPORT void VPKC_CALL vpkc_app_set_hook_after_install(vpkc_hook_callback_t cbAfterInstall);
VPKC_EXPORT void VPKC_CALL vpkc_app_set_hook_before_uninstall(vpkc_hook_callback_t cbBeforeUninstall);
VPKC_EXPORT void VPKC_CALL vpkc_app_set_hook_before_update(vpkc_hook_callback_t cbBeforeUpdate);
@@ -126,259 +128,302 @@ VPKC_EXPORT void VPKC_CALL vpkc_free_asset(vpkc_asset_t* pAsset);
#ifdef __cplusplus
namespace Velopack {
struct VelopackAsset {
std::string PackageId;
std::string Version;
std::string Type;
std::string FileName;
std::string SHA1;
std::string SHA256;
uint64_t Size;
std::string NotesMarkdown;
std::string NotesHtml;
};
struct UpdateInfo {
VelopackAsset TargetFullRelease;
bool IsDowngrade;
};
static inline void throw_last_error() {
size_t neededSize = vpkc_get_last_error(nullptr, 0);
std::string strError(neededSize, '\0');
vpkc_get_last_error(&strError[0], neededSize);
throw std::runtime_error(strError);
}
struct UpdateOptions {
bool AllowVersionDowngrade;
std::string ExplicitChannel;
};
static inline std::string to_cppstring(const char* psz) {
return psz == nullptr ? "" : psz;
}
struct VelopackLocator {
std::string RootAppDir;
std::string UpdateExePath;
std::string PackagesDir;
std::string ManifestPath;
std::string CurrentBinaryDir;
bool IsPortable;
};
static inline char* to_cstring(const std::string& str) {
return const_cast<char*>(str.c_str());
}
static inline void throw_last_error() {
size_t neededSize = vpkc_get_last_error(nullptr, 0);
std::string strError(neededSize, '\0');
vpkc_get_last_error(&strError[0], neededSize);
throw std::runtime_error(strError);
}
static inline char* to_cstring_opt(const std::optional<std::string>& str) {
return str.has_value() ? to_cstring(str.value()) : nullptr;
}
static inline vpkc_locator_t to_vpkc(const VelopackLocator& locator) {
return {
const_cast<char*>(locator.RootAppDir.c_str()),
const_cast<char*>(locator.UpdateExePath.c_str()),
const_cast<char*>(locator.PackagesDir.c_str()),
const_cast<char*>(locator.ManifestPath.c_str()),
const_cast<char*>(locator.CurrentBinaryDir.c_str()),
locator.IsPortable
};
}
static inline std::optional<std::string> to_cppstring_opt(const char* psz) {
return psz == nullptr ? std::nullopt : std::optional<std::string>(psz);
}
static inline vpkc_options_t to_vpkc(const UpdateOptions& options) {
return {
options.AllowVersionDowngrade,
const_cast<char*>(options.ExplicitChannel.c_str())
};
}
static inline bool to_cppbool(bool b) { return b; }
static inline bool to_cbool(bool b) { return b; }
static inline uint64_t to_cu64(uint64_t i) { return i; }
static inline uint64_t to_cppu64(uint64_t i) { return i; }
static inline vpkc_asset_t to_vpkc(const VelopackAsset& asset) {
return {
const_cast<char*>(asset.PackageId.c_str()),
const_cast<char*>(asset.Version.c_str()),
const_cast<char*>(asset.Type.c_str()),
const_cast<char*>(asset.FileName.c_str()),
const_cast<char*>(asset.SHA1.c_str()),
const_cast<char*>(asset.SHA256.c_str()),
asset.Size,
const_cast<char*>(asset.NotesMarkdown.c_str()),
const_cast<char*>(asset.NotesHtml.c_str())
};
}
// !! AUTO-GENERATED-START CPP_TYPES
struct VelopackLocatorConfig {
std::string RootAppDir;
std::string UpdateExePath;
std::string PackagesDir;
std::string ManifestPath;
std::string CurrentBinaryDir;
bool IsPortable;
};
static inline VelopackAsset from_vpkc(const vpkc_asset_t& asset) {
return {
asset.PackageId,
asset.Version,
asset.Type,
asset.FileName,
asset.SHA1,
asset.SHA256,
asset.Size,
asset.NotesMarkdown,
asset.NotesHtml
};
}
static inline vpkc_update_info_t to_vpkc(const UpdateInfo& update) {
return {
to_vpkc(update.TargetFullRelease),
update.IsDowngrade
};
}
static inline UpdateInfo from_vpkc(const vpkc_update_info_t& update) {
return {
from_vpkc(update.TargetFullRelease),
update.IsDowngrade
};
}
class VelopackApp {
private:
VelopackApp();
public:
static VelopackApp Build() {
return VelopackApp();
};
VelopackApp& SetAutoApplyOnStartup(bool bAutoApply) {
vpkc_app_set_auto_apply_on_startup(bAutoApply);
return *this;
};
VelopackApp& SetArgs(const std::vector<std::string>& args) {
char** pArgs = new char*[args.size()];
for (size_t i = 0; i < args.size(); i++) {
pArgs[i] = new char[args[i].size() + 1];
strcpy_s(pArgs[i], args[i].size() + 1, args[i].c_str());
}
vpkc_app_set_args(pArgs, args.size());
// Free all the memory
for (size_t i = 0; i < args.size(); i++) {
delete[] pArgs[i];
}
delete[] pArgs;
return *this;
};
VelopackApp& SetLocator(const VelopackLocator& locator) {
vpkc_locator_t vpkc_locator = to_vpkc(locator);
vpkc_app_set_locator(const_cast<vpkc_locator_t*>(&vpkc_locator));
return *this;
};
VelopackApp& OnAfterInstall(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_after_install(cbInstall);
return *this;
};
VelopackApp& OnBeforeUninstall(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_before_uninstall(cbInstall);
return *this;
};
VelopackApp& OnBeforeUpdate(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_before_update(cbInstall);
return *this;
};
VelopackApp& OnAfterUpdate(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_after_update(cbInstall);
return *this;
};
VelopackApp& OnFirstRun(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_first_run(cbInstall);
return *this;
};
VelopackApp& OnRestarted(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_restarted(cbInstall);
return *this;
};
void Run() {
vpkc_app_run();
};
};
class UpdateManager {
private:
vpkc_update_manager_t m_pManager;
public:
UpdateManager(const std::string& urlOrPath, const UpdateOptions* options, const VelopackLocator* locator) {
vpkc_options_t* pOptions = nullptr;
if (options != nullptr) {
vpkc_options_t vpkc_options = to_vpkc(*options);
pOptions = const_cast<vpkc_options_t*>(&vpkc_options);
}
vpkc_locator_t* pLocator = nullptr;
if (locator != nullptr) {
vpkc_locator_t vpkc_locator = to_vpkc(*locator);
pLocator = const_cast<vpkc_locator_t*>(&vpkc_locator);
}
if (0 != vpkc_new_update_manager(urlOrPath.c_str(), pOptions, pLocator, &m_pManager)) {
throw_last_error();
}
};
~UpdateManager() {
vpkc_free_update_manager(&m_pManager);
};
bool IsPortable() noexcept {
return vpkc_is_portable(&m_pManager);
};
std::string GetCurrentVersion() noexcept {
size_t neededSize = vpkc_get_current_version(&m_pManager, nullptr, 0);
std::string strVersion(neededSize, '\0');
vpkc_get_current_version(&m_pManager, &strVersion[0], neededSize);
return strVersion;
};
std::string GetAppId() noexcept {
size_t neededSize = vpkc_get_app_id(&m_pManager, nullptr, 0);
std::string strId(neededSize, '\0');
vpkc_get_app_id(&m_pManager, &strId[0], neededSize);
return strId;
};
std::optional<VelopackAsset> UpdatePendingRestart() noexcept {
vpkc_asset_t asset;
if (vpkc_update_pending_restart(&m_pManager, &asset)) {
VelopackAsset cpp_asset = from_vpkc(asset);
vpkc_free_asset(&asset);
return cpp_asset;
}
return std::nullopt;
};
std::optional<UpdateInfo> CheckForUpdates() {
vpkc_update_info_t update;
vpkc_update_check_t result = vpkc_check_for_updates(&m_pManager, &update);
switch (result) {
case vpkc_update_check_t::ERROR:
throw_last_error();
return std::nullopt;
case vpkc_update_check_t::NO_UPDATE_AVAILABLE:
return std::nullopt;
case vpkc_update_check_t::UPDATE_AVAILABLE:
UpdateInfo cpp_info = from_vpkc(update);
vpkc_free_update_info(&update);
return cpp_info;
}
};
void DownloadUpdates(const UpdateInfo& update, vpkc_progress_callback_t progress) {
vpkc_update_info_t vpkc_update = to_vpkc(update);
if (!vpkc_download_updates(&m_pManager, &vpkc_update, progress)) {
throw_last_error();
}
};
void WaitExitThenApplyUpdate(const VelopackAsset& asset, bool silent, bool restart, std::vector<std::string> restartArgs) {
char** pRestartArgs = new char*[restartArgs.size()];
for (size_t i = 0; i < restartArgs.size(); i++) {
pRestartArgs[i] = new char[restartArgs[i].size() + 1];
strcpy_s(pRestartArgs[i], restartArgs[i].size() + 1, restartArgs[i].c_str());
}
vpkc_asset_t vpkc_asset = to_vpkc(asset);
bool result = vpkc_wait_exit_then_apply_update(&m_pManager, &vpkc_asset, silent, restart, pRestartArgs, restartArgs.size());
// Free all the memory
for (size_t i = 0; i < restartArgs.size(); i++) {
delete[] pRestartArgs[i];
}
delete[] pRestartArgs;
if (!result) {
throw_last_error();
}
};
void WaitExitThenApplyUpdate(const UpdateInfo& asset, bool silent, bool restart, std::vector<std::string> restartArgs) {
this->WaitExitThenApplyUpdate(asset.TargetFullRelease, silent, restart, restartArgs);
};
static inline vpkc_locator_config_t to_c(const VelopackLocatorConfig& dto) {
return {
to_cstring(dto.RootAppDir),
to_cstring(dto.UpdateExePath),
to_cstring(dto.PackagesDir),
to_cstring(dto.ManifestPath),
to_cstring(dto.CurrentBinaryDir),
to_cbool(dto.IsPortable),
};
}
static inline VelopackLocatorConfig to_cpp(const vpkc_locator_config_t& dto) {
return {
to_cppstring(dto.RootAppDir),
to_cppstring(dto.UpdateExePath),
to_cppstring(dto.PackagesDir),
to_cppstring(dto.ManifestPath),
to_cppstring(dto.CurrentBinaryDir),
to_cppbool(dto.IsPortable),
};
}
struct VelopackAsset {
std::string PackageId;
std::string Version;
std::string Type;
std::string FileName;
std::string SHA1;
std::string SHA256;
uint64_t Size;
std::string NotesMarkdown;
std::string NotesHtml;
};
static inline vpkc_asset_t to_c(const VelopackAsset& dto) {
return {
to_cstring(dto.PackageId),
to_cstring(dto.Version),
to_cstring(dto.Type),
to_cstring(dto.FileName),
to_cstring(dto.SHA1),
to_cstring(dto.SHA256),
to_cu64(dto.Size),
to_cstring(dto.NotesMarkdown),
to_cstring(dto.NotesHtml),
};
}
static inline VelopackAsset to_cpp(const vpkc_asset_t& dto) {
return {
to_cppstring(dto.PackageId),
to_cppstring(dto.Version),
to_cppstring(dto.Type),
to_cppstring(dto.FileName),
to_cppstring(dto.SHA1),
to_cppstring(dto.SHA256),
to_cppu64(dto.Size),
to_cppstring(dto.NotesMarkdown),
to_cppstring(dto.NotesHtml),
};
}
struct UpdateInfo {
VelopackAsset TargetFullRelease;
bool IsDowngrade;
};
static inline vpkc_update_info_t to_c(const UpdateInfo& dto) {
return {
to_c(dto.TargetFullRelease),
to_cbool(dto.IsDowngrade),
};
}
static inline UpdateInfo to_cpp(const vpkc_update_info_t& dto) {
return {
to_cpp(dto.TargetFullRelease),
to_cppbool(dto.IsDowngrade),
};
}
struct UpdateOptions {
bool AllowVersionDowngrade;
std::optional<std::string> ExplicitChannel;
};
static inline vpkc_update_options_t to_c(const UpdateOptions& dto) {
return {
to_cbool(dto.AllowVersionDowngrade),
to_cstring_opt(dto.ExplicitChannel),
};
}
static inline UpdateOptions to_cpp(const vpkc_update_options_t& dto) {
return {
to_cppbool(dto.AllowVersionDowngrade),
to_cppstring_opt(dto.ExplicitChannel),
};
}
// !! AUTO-GENERATED-END CPP_TYPES
class VelopackApp {
private:
VelopackApp();
public:
static VelopackApp Build() {
return VelopackApp();
};
VelopackApp& SetAutoApplyOnStartup(bool bAutoApply) {
vpkc_app_set_auto_apply_on_startup(bAutoApply);
return *this;
};
VelopackApp& SetArgs(const std::vector<std::string>& args) {
char** pArgs = new char*[args.size()];
for (size_t i = 0; i < args.size(); i++) {
pArgs[i] = new char[args[i].size() + 1];
strcpy_s(pArgs[i], args[i].size() + 1, args[i].c_str());
}
vpkc_app_set_args(pArgs, args.size());
// Free all the memory
for (size_t i = 0; i < args.size(); i++) {
delete[] pArgs[i];
}
delete[] pArgs;
return *this;
};
VelopackApp& SetLocator(const VelopackLocatorConfig& locator) {
vpkc_locator_config_t vpkc_locator = to_c(locator);
vpkc_app_set_locator(&vpkc_locator);
return *this;
};
VelopackApp& OnAfterInstall(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_after_install(cbInstall);
return *this;
};
VelopackApp& OnBeforeUninstall(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_before_uninstall(cbInstall);
return *this;
};
VelopackApp& OnBeforeUpdate(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_before_update(cbInstall);
return *this;
};
VelopackApp& OnAfterUpdate(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_after_update(cbInstall);
return *this;
};
VelopackApp& OnFirstRun(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_first_run(cbInstall);
return *this;
};
VelopackApp& OnRestarted(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_restarted(cbInstall);
return *this;
};
void Run() {
vpkc_app_run();
};
};
class UpdateManager {
private:
vpkc_update_manager_t m_pManager;
public:
UpdateManager(const std::string& urlOrPath, 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;
}
if (0 != vpkc_new_update_manager(urlOrPath.c_str(), pOptions, pLocator, &m_pManager)) {
throw_last_error();
}
};
~UpdateManager() {
vpkc_free_update_manager(&m_pManager);
};
bool IsPortable() noexcept {
return vpkc_is_portable(&m_pManager);
};
std::string GetCurrentVersion() noexcept {
size_t neededSize = vpkc_get_current_version(&m_pManager, nullptr, 0);
std::string strVersion(neededSize, '\0');
vpkc_get_current_version(&m_pManager, &strVersion[0], neededSize);
return strVersion;
};
std::string GetAppId() noexcept {
size_t neededSize = vpkc_get_app_id(&m_pManager, nullptr, 0);
std::string strId(neededSize, '\0');
vpkc_get_app_id(&m_pManager, &strId[0], neededSize);
return strId;
};
std::optional<VelopackAsset> UpdatePendingRestart() noexcept {
vpkc_asset_t asset;
if (vpkc_update_pending_restart(&m_pManager, &asset)) {
VelopackAsset cpp_asset = to_cpp(asset);
vpkc_free_asset(&asset);
return cpp_asset;
}
return std::nullopt;
};
std::optional<UpdateInfo> CheckForUpdates() {
vpkc_update_info_t update;
vpkc_update_check_t result = vpkc_check_for_updates(&m_pManager, &update);
switch (result) {
case vpkc_update_check_t::ERROR:
throw_last_error();
return std::nullopt;
case vpkc_update_check_t::NO_UPDATE_AVAILABLE:
return std::nullopt;
case vpkc_update_check_t::UPDATE_AVAILABLE:
UpdateInfo cpp_info = to_cpp(update);
vpkc_free_update_info(&update);
return cpp_info;
}
};
void DownloadUpdates(const UpdateInfo& update, vpkc_progress_callback_t progress) {
vpkc_update_info_t vpkc_update = to_c(update);
if (!vpkc_download_updates(&m_pManager, &vpkc_update, progress)) {
throw_last_error();
}
};
void WaitExitThenApplyUpdate(const VelopackAsset& asset, bool silent, bool restart, std::vector<std::string> restartArgs) {
char** pRestartArgs = new char*[restartArgs.size()];
for (size_t i = 0; i < restartArgs.size(); i++) {
pRestartArgs[i] = new char[restartArgs[i].size() + 1];
strcpy_s(pRestartArgs[i], restartArgs[i].size() + 1, restartArgs[i].c_str());
}
vpkc_asset_t vpkc_asset = to_c(asset);
bool result = vpkc_wait_exit_then_apply_update(&m_pManager, &vpkc_asset, silent, restart, pRestartArgs, restartArgs.size());
// Free all the memory
for (size_t i = 0; i < restartArgs.size(); i++) {
delete[] pRestartArgs[i];
}
delete[] pRestartArgs;
if (!result) {
throw_last_error();
}
};
void WaitExitThenApplyUpdate(const UpdateInfo& asset, bool silent, bool restart, std::vector<std::string> restartArgs) {
this->WaitExitThenApplyUpdate(asset.TargetFullRelease, silent, restart, restartArgs);
};
};
} // namespace Velopack
#endif // __cplusplus
#endif // VELOPACK_H

View File

@@ -1,5 +1,205 @@
// Uncomment to enable debug type checking
// #pragma include_alias( "velopack_libc/src/lib.rs.h", "../../../target/cxxbridge/velopack_libc/src/lib.rs.h" )
// #pragma include_alias( "velopack_libc/include/Velopack.h", "../include/Velopack.h" )
// #pragma include_alias( "velopack_libc/src/bridge.hpp", "bridge.hpp" )
// #pragma include_alias( "rust/cxx.h", "../../../target/cxxbridge/rust/cxx.h" )
#include "velopack_libc/src/lib.rs.h"
static inline std::string to_bridgestring(const char* psz) {
return psz == nullptr ? "" : psz;
}
static inline char* to_cstring(const std::string& str) {
return const_cast<char*>(str.c_str());
}
static inline char* to_cstring_opt(const std::optional<std::string>& str) {
return str.has_value() ? to_cstring(str.value()) : nullptr;
}
static inline StringOption to_bridgestring_opt(const char* psz) {
StringOption opt;
if (psz == nullptr) {
opt.has_data = false;
return opt;
}
opt.has_data = true;
opt.data = psz;
return opt;
}
static inline void allocate_string(::rust::String& str, char** ppsz) {
*ppsz = _strdup(str.c_str());
}
static inline void allocate_string_opt(StringOption str, char** ppsz) {
if (str.has_data) {
*ppsz = _strdup(str.data.c_str());
} else {
*ppsz = nullptr;
}
}
// !! AUTO-GENERATED-START BRIDGE_MAPPING
static inline VelopackLocatorConfigDto to_bridge(vpkc_locator_config_t* pDto) {
if (pDto == nullptr) { return {}; }
return {
to_bridgestring(pDto->RootAppDir),
to_bridgestring(pDto->UpdateExePath),
to_bridgestring(pDto->PackagesDir),
to_bridgestring(pDto->ManifestPath),
to_bridgestring(pDto->CurrentBinaryDir),
pDto->IsPortable,
};
}
static inline VelopackLocatorConfigDtoOption to_bridge_opt(vpkc_locator_config_t* pDto) {
VelopackLocatorConfigDtoOption opt;
if (pDto == nullptr) {
opt.has_data = false;
return opt;
}
opt.has_data = true;
opt.data = to_bridge(pDto);
return opt;
}
static inline void allocate_velopacklocatorconfig(VelopackLocatorConfigDto bridgeDto, vpkc_locator_config_t* pDto) {
if (pDto == nullptr) { return; }
allocate_string(bridgeDto.RootAppDir, &pDto->RootAppDir);
allocate_string(bridgeDto.UpdateExePath, &pDto->UpdateExePath);
allocate_string(bridgeDto.PackagesDir, &pDto->PackagesDir);
allocate_string(bridgeDto.ManifestPath, &pDto->ManifestPath);
allocate_string(bridgeDto.CurrentBinaryDir, &pDto->CurrentBinaryDir);
pDto->IsPortable = bridgeDto.IsPortable;
}
static inline void free_velopacklocatorconfig(vpkc_locator_config_t* pDto) {
if (pDto == nullptr) { return; }
free(pDto->RootAppDir);
free(pDto->UpdateExePath);
free(pDto->PackagesDir);
free(pDto->ManifestPath);
free(pDto->CurrentBinaryDir);
}
static inline VelopackAssetDto to_bridge(vpkc_asset_t* pDto) {
if (pDto == nullptr) { return {}; }
return {
to_bridgestring(pDto->PackageId),
to_bridgestring(pDto->Version),
to_bridgestring(pDto->Type),
to_bridgestring(pDto->FileName),
to_bridgestring(pDto->SHA1),
to_bridgestring(pDto->SHA256),
pDto->Size,
to_bridgestring(pDto->NotesMarkdown),
to_bridgestring(pDto->NotesHtml),
};
}
static inline VelopackAssetDtoOption to_bridge_opt(vpkc_asset_t* pDto) {
VelopackAssetDtoOption opt;
if (pDto == nullptr) {
opt.has_data = false;
return opt;
}
opt.has_data = true;
opt.data = to_bridge(pDto);
return opt;
}
static inline void allocate_velopackasset(VelopackAssetDto bridgeDto, vpkc_asset_t* pDto) {
if (pDto == nullptr) { return; }
allocate_string(bridgeDto.PackageId, &pDto->PackageId);
allocate_string(bridgeDto.Version, &pDto->Version);
allocate_string(bridgeDto.Type, &pDto->Type);
allocate_string(bridgeDto.FileName, &pDto->FileName);
allocate_string(bridgeDto.SHA1, &pDto->SHA1);
allocate_string(bridgeDto.SHA256, &pDto->SHA256);
pDto->Size = bridgeDto.Size;
allocate_string(bridgeDto.NotesMarkdown, &pDto->NotesMarkdown);
allocate_string(bridgeDto.NotesHtml, &pDto->NotesHtml);
}
static inline void free_velopackasset(vpkc_asset_t* pDto) {
if (pDto == nullptr) { return; }
free(pDto->PackageId);
free(pDto->Version);
free(pDto->Type);
free(pDto->FileName);
free(pDto->SHA1);
free(pDto->SHA256);
free(pDto->NotesMarkdown);
free(pDto->NotesHtml);
}
static inline UpdateInfoDto to_bridge(vpkc_update_info_t* pDto) {
if (pDto == nullptr) { return {}; }
return {
to_bridge(&pDto->TargetFullRelease),
pDto->IsDowngrade,
};
}
static inline UpdateInfoDtoOption to_bridge_opt(vpkc_update_info_t* pDto) {
UpdateInfoDtoOption opt;
if (pDto == nullptr) {
opt.has_data = false;
return opt;
}
opt.has_data = true;
opt.data = to_bridge(pDto);
return opt;
}
static inline void allocate_updateinfo(UpdateInfoDto bridgeDto, vpkc_update_info_t* pDto) {
if (pDto == nullptr) { return; }
allocate_velopackasset(bridgeDto.TargetFullRelease, &pDto->TargetFullRelease);
pDto->IsDowngrade = bridgeDto.IsDowngrade;
}
static inline void free_updateinfo(vpkc_update_info_t* pDto) {
if (pDto == nullptr) { return; }
free_velopackasset(&pDto->TargetFullRelease);
}
static inline UpdateOptionsDto to_bridge(vpkc_update_options_t* pDto) {
if (pDto == nullptr) { return {}; }
return {
pDto->AllowVersionDowngrade,
to_bridgestring_opt(pDto->ExplicitChannel),
};
}
static inline UpdateOptionsDtoOption to_bridge_opt(vpkc_update_options_t* pDto) {
UpdateOptionsDtoOption opt;
if (pDto == nullptr) {
opt.has_data = false;
return opt;
}
opt.has_data = true;
opt.data = to_bridge(pDto);
return opt;
}
static inline void allocate_updateoptions(UpdateOptionsDto bridgeDto, vpkc_update_options_t* pDto) {
if (pDto == nullptr) { return; }
pDto->AllowVersionDowngrade = bridgeDto.AllowVersionDowngrade;
allocate_string_opt(bridgeDto.ExplicitChannel, &pDto->ExplicitChannel);
}
static inline void free_updateoptions(vpkc_update_options_t* pDto) {
if (pDto == nullptr) { return; }
free(pDto->ExplicitChannel);
}
// !! AUTO-GENERATED-END BRIDGE_MAPPING
// Error handling
char* lastError;
LoggerCallbackManager logMgr{};
@@ -33,81 +233,12 @@ static inline void clear_last_error() {
}
}
static inline void copy_to_locator_option(vpkc_locator_t* pLocator, LocatorConfigOption& locator) {
if (pLocator) {
locator.has_data = true;
locator.data.RootAppDir = pLocator->RootAppDir;
locator.data.UpdateExePath = pLocator->UpdateExePath;
locator.data.PackagesDir = pLocator->PackagesDir;
locator.data.ManifestPath = pLocator->ManifestPath;
locator.data.CurrentBinaryDir = pLocator->CurrentBinaryDir;
locator.data.IsPortable = pLocator->IsPortable;
} else {
locator.has_data = false;
}
}
static inline void copy_to_asset_dto(vpkc_asset_t* pAsset, AssetDto& asset) {
if (pAsset != nullptr) {
asset.PackageId = pAsset->PackageId;
asset.Version = pAsset->Version;
asset.Type = pAsset->Type;
asset.FileName = pAsset->FileName;
asset.SHA1 = pAsset->SHA1;
asset.SHA256 = pAsset->SHA256;
asset.NotesMarkdown = pAsset->NotesMarkdown;
asset.NotesHtml = pAsset->NotesHtml;
asset.Size = pAsset->Size;
}
}
static inline void copy_to_update_info_dto(vpkc_update_info_t* pUpdate, UpdateInfoDto& update) {
if (pUpdate != nullptr) {
copy_to_asset_dto(&pUpdate->TargetFullRelease, update.TargetFullRelease);
update.IsDowngrade = pUpdate->IsDowngrade;
}
}
static inline void copy_to_asset_pointer(AssetDto& asset, vpkc_asset_t* pAsset) {
if (pAsset != nullptr) {
pAsset->PackageId = _strdup(asset.PackageId.c_str());
pAsset->Version = _strdup(asset.Version.c_str());
pAsset->Type = _strdup(asset.Type.c_str());
pAsset->FileName = _strdup(asset.FileName.c_str());
pAsset->SHA1 = _strdup(asset.SHA1.c_str());
pAsset->SHA256 = _strdup(asset.SHA256.c_str());
pAsset->NotesMarkdown = _strdup(asset.NotesMarkdown.c_str());
pAsset->NotesHtml = _strdup(asset.NotesHtml.c_str());
pAsset->Size = asset.Size;
}
}
static inline void copy_to_asset_pointer(AssetOption& asset, vpkc_asset_t* pAsset) {
if (asset.has_data && pAsset != nullptr) {
copy_to_asset_pointer(asset.data, pAsset);
}
}
static inline void copy_to_update_info_pointer(UpdateInfoOption& update, vpkc_update_info_t* pUpdate) {
if (update.has_data && pUpdate != nullptr) {
copy_to_asset_pointer(update.data.TargetFullRelease, &pUpdate->TargetFullRelease);
pUpdate->IsDowngrade = update.data.IsDowngrade;
}
}
// Update Manager
VPKC_EXPORT bool VPKC_CALL vpkc_new_update_manager(const char* pszUrlOrString, const vpkc_options_t* pOptions, vpkc_locator_t* pLocator, vpkc_update_manager_t* pManager) {
VPKC_EXPORT bool VPKC_CALL vpkc_new_update_manager(const char* pszUrlOrString, vpkc_update_options_t* pOptions, vpkc_locator_config_t* pLocator, vpkc_update_manager_t* pManager) {
clear_last_error();
try {
LocatorConfigOption locator{};
UpdateOptionsDto options{};
if (pOptions) {
options.AllowVersionDowngrade = pOptions->AllowVersionDowngrade;
if (pOptions->ExplicitChannel) {
options.ExplicitChannel = pOptions->ExplicitChannel;
}
}
copy_to_locator_option(pLocator, locator);
VelopackLocatorConfigDtoOption locator = to_bridge_opt(pLocator);
UpdateOptionsDtoOption options = to_bridge_opt(pOptions);
::rust::Box<::UpdateManagerOpaque> manager = bridge_new_update_manager(pszUrlOrString, options, locator);
UpdateManagerOpaque* pOpaque = manager.into_raw();
@@ -162,21 +293,20 @@ VPKC_EXPORT bool VPKC_CALL vpkc_is_portable(vpkc_update_manager_t* pManager) {
}
VPKC_EXPORT bool VPKC_CALL vpkc_update_pending_restart(vpkc_update_manager_t* pManager, vpkc_asset_t* pAsset) {
UpdateManagerOpaque* pOpaque = reinterpret_cast<UpdateManagerOpaque*>(*pManager);
AssetOption asset = bridge_update_pending_restart(*pOpaque);
VelopackAssetDtoOption asset = bridge_update_pending_restart(*pOpaque);
if (asset.has_data) {
copy_to_asset_pointer(asset, pAsset);
allocate_velopackasset(asset.data, pAsset);
return true;
}
return false;
}
VPKC_EXPORT vpkc_update_check_t VPKC_CALL vpkc_check_for_updates(vpkc_update_manager_t* pManager, vpkc_update_info_t* pUpdate) {
clear_last_error();
try {
UpdateManagerOpaque* pOpaque = reinterpret_cast<UpdateManagerOpaque*>(*pManager);
UpdateInfoOption update = bridge_check_for_updates(*pOpaque);
UpdateInfoDtoOption update = bridge_check_for_updates(*pOpaque);
if (update.has_data) {
copy_to_update_info_pointer(update, pUpdate);
allocate_updateinfo(update.data, pUpdate);
return vpkc_update_check_t::UPDATE_AVAILABLE;
}
return vpkc_update_check_t::NO_UPDATE_AVAILABLE;
@@ -189,14 +319,12 @@ VPKC_EXPORT vpkc_update_check_t VPKC_CALL vpkc_check_for_updates(vpkc_update_man
VPKC_EXPORT bool VPKC_CALL vpkc_download_updates(vpkc_update_manager_t* pManager, vpkc_update_info_t* pUpdate, vpkc_progress_callback_t cbProgress) {
clear_last_error();
try {
UpdateManagerOpaque* pOpaque = reinterpret_cast<UpdateManagerOpaque*>(*pManager);
UpdateInfoDto update{};
if (!pUpdate) {
throw new std::runtime_error("pUpdate is a required parameter");
}
copy_to_update_info_dto(pUpdate, update);
UpdateManagerOpaque* pOpaque = reinterpret_cast<UpdateManagerOpaque*>(*pManager);
UpdateInfoDto update = to_bridge(pUpdate);
DownloadCallbackManager download{};
download.progress_cb = cbProgress;
@@ -212,14 +340,12 @@ VPKC_EXPORT bool VPKC_CALL vpkc_download_updates(vpkc_update_manager_t* pManager
VPKC_EXPORT bool VPKC_CALL vpkc_wait_exit_then_apply_update(vpkc_update_manager_t* pManager, vpkc_asset_t* pAsset, bool bSilent, bool bRestart, char** pRestartArgs, size_t cRestartArgs) {
clear_last_error();
try {
UpdateManagerOpaque* pOpaque = reinterpret_cast<UpdateManagerOpaque*>(*pManager);
AssetDto asset{};
if (!pAsset) {
throw new std::runtime_error("pAsset is a required parameter");
}
copy_to_asset_dto(pAsset, asset);
UpdateManagerOpaque* pOpaque = reinterpret_cast<UpdateManagerOpaque*>(*pManager);
VelopackAssetDto asset = to_bridge(pAsset);
::rust::Vec<::rust::String> restartArgs{};
for (size_t i = 0; i < cRestartArgs; i++) {
@@ -238,7 +364,7 @@ VPKC_EXPORT bool VPKC_CALL vpkc_wait_exit_then_apply_update(vpkc_update_manager_
// VelopackApp
bool autoApply = true;
StringArrayOption args{};
LocatorConfigOption locator{};
VelopackLocatorConfigDtoOption locator{};
HookCallbackManager hooks{};
VPKC_EXPORT void VPKC_CALL vpkc_app_set_auto_apply_on_startup(bool bAutoApply) {
@@ -251,8 +377,8 @@ VPKC_EXPORT void VPKC_CALL vpkc_app_set_args(char** pArgs, size_t cArgs) {
args.data.push_back(pArgs[i]);
}
}
VPKC_EXPORT void VPKC_CALL vpkc_app_set_locator(vpkc_locator_t* pLocator) {
copy_to_locator_option(pLocator, locator);
VPKC_EXPORT void VPKC_CALL vpkc_app_set_locator(vpkc_locator_config_t* pLocator) {
locator = to_bridge_opt(pLocator);
}
VPKC_EXPORT void VPKC_CALL vpkc_app_set_hook_after_install(vpkc_hook_callback_t cbAfterInstall) {
hooks.after_install = cbAfterInstall;
@@ -284,45 +410,11 @@ VPKC_EXPORT void VPKC_CALL vpkc_set_log(vpkc_log_callback_t cbLog) {
VPKC_EXPORT void VPKC_CALL vpkc_free_update_manager(vpkc_update_manager_t* pManager) {
UpdateManagerOpaque* pOpaque = reinterpret_cast<UpdateManagerOpaque*>(*pManager);
auto box = ::rust::Box<::UpdateManagerOpaque>::from_raw(pOpaque);
// this will free when the box goes out of scope
}
VPKC_EXPORT void VPKC_CALL vpkc_free_update_info(vpkc_update_info_t* pUpdateInfo) {
if (pUpdateInfo != nullptr) {
vpkc_free_asset(&pUpdateInfo->TargetFullRelease);
}
free_updateinfo(pUpdateInfo);
}
VPKC_EXPORT void VPKC_CALL vpkc_free_asset(vpkc_asset_t* pAsset) {
if (pAsset != nullptr) {
if (pAsset->PackageId) {
free(pAsset->PackageId);
pAsset->PackageId = nullptr;
}
if (pAsset->Version) {
free(pAsset->Version);
pAsset->Version = nullptr;
}
if (pAsset->Type) {
free(pAsset->Type);
pAsset->Type = nullptr;
}
if (pAsset->FileName) {
free(pAsset->FileName);
pAsset->FileName = nullptr;
}
if (pAsset->SHA1) {
free(pAsset->SHA1);
pAsset->SHA1 = nullptr;
}
if (pAsset->SHA256) {
free(pAsset->SHA256);
pAsset->SHA256 = nullptr;
}
if (pAsset->NotesMarkdown) {
free(pAsset->NotesMarkdown);
pAsset->NotesMarkdown = nullptr;
}
if (pAsset->NotesHtml) {
free(pAsset->NotesHtml);
pAsset->NotesHtml = nullptr;
}
}
free_velopackasset(pAsset);
}

View File

@@ -1,26 +1,47 @@
#![allow(non_snake_case)]
mod map;
use map::*;
use anyhow::{bail, Result};
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};
use log::{Level, Log, Metadata, Record};
use velopack::{
locator::VelopackLocatorConfig,
UpdateManager,
UpdateCheck,
UpdateOptions as VelopackUpdateOptions,
UpdateInfo as VelopackUpdateInfo,
VelopackAsset,
VelopackApp,
Error as VelopackError,
sources,
};
use std::sync::atomic::{AtomicUsize, Ordering};
use velopack::{sources, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp};
#[cxx::bridge]
mod ffi {
// Shared structs with fields visible to both languages.
#[derive(Default)]
pub struct AssetDto {
pub struct StringOption {
pub data: String,
pub has_data: bool,
}
#[derive(Default)]
pub struct StringArrayOption {
pub data: Vec<String>,
pub has_data: bool,
}
// !! AUTO-GENERATED-START BRIDGE_DTOS
#[derive(Default)]
pub struct VelopackLocatorConfigDto {
pub RootAppDir: String,
pub UpdateExePath: String,
pub PackagesDir: String,
pub ManifestPath: String,
pub CurrentBinaryDir: String,
pub IsPortable: bool,
}
#[derive(Default)]
pub struct VelopackLocatorConfigDtoOption {
pub data: VelopackLocatorConfigDto,
pub has_data: bool,
}
#[derive(Default)]
pub struct VelopackAssetDto {
pub PackageId: String,
pub Version: String,
pub Type: String,
@@ -32,50 +53,42 @@ mod ffi {
pub NotesHtml: String,
}
pub struct AssetOption {
pub data: AssetDto,
#[derive(Default)]
pub struct VelopackAssetDtoOption {
pub data: VelopackAssetDto,
pub has_data: bool,
}
#[derive(Default)]
pub struct UpdateInfoDto {
pub TargetFullRelease: AssetDto,
pub TargetFullRelease: VelopackAssetDto,
pub IsDowngrade: bool,
}
pub struct UpdateInfoOption {
#[derive(Default)]
pub struct UpdateInfoDtoOption {
pub data: UpdateInfoDto,
pub has_data: bool,
}
pub struct LocatorConfigDto {
pub RootAppDir: String,
pub UpdateExePath: String,
pub PackagesDir: String,
pub ManifestPath: String,
pub CurrentBinaryDir: String,
pub IsPortable: bool,
}
pub struct LocatorConfigOption {
pub data: LocatorConfigDto,
pub has_data: bool,
}
#[derive(Default)]
pub struct UpdateOptionsDto {
pub AllowVersionDowngrade: bool,
pub ExplicitChannel: String,
pub ExplicitChannel: StringOption,
}
pub struct StringArrayOption {
pub data: Vec<String>,
#[derive(Default)]
pub struct UpdateOptionsDtoOption {
pub data: UpdateOptionsDto,
pub has_data: bool,
}
// !! AUTO-GENERATED-END BRIDGE_DTOS
// C++ types and signatures exposed to Rust.
unsafe extern "C++" {
include!("velopack_libc/include/Velopack.h");
include!("velopack_libc/src/bridge.hpp");
type HookCallbackManager;
fn install_hook(self: &HookCallbackManager, app_version: String);
fn update_hook(self: &HookCallbackManager, app_version: String);
@@ -83,8 +96,10 @@ mod ffi {
fn uninstall_hook(self: &HookCallbackManager, app_version: String);
fn firstrun_hook(self: &HookCallbackManager, app_version: String);
fn restarted_hook(self: &HookCallbackManager, app_version: String);
type DownloadCallbackManager;
fn download_progress(self: &DownloadCallbackManager, progress: i16);
type LoggerCallbackManager;
fn log(self: &LoggerCallbackManager, level: String, message: String);
}
@@ -92,15 +107,34 @@ mod ffi {
// Rust types and signatures exposed to C++.
extern "Rust" {
type UpdateManagerOpaque;
fn bridge_new_update_manager(url_or_path: &String, options: &UpdateOptionsDto, locator: &LocatorConfigOption) -> Result<Box<UpdateManagerOpaque>>;
fn bridge_new_update_manager(
url_or_path: &String,
options: &UpdateOptionsDtoOption,
locator: &VelopackLocatorConfigDtoOption,
) -> Result<Box<UpdateManagerOpaque>>;
fn bridge_get_current_version(manager: &UpdateManagerOpaque) -> String;
fn bridge_get_app_id(manager: &UpdateManagerOpaque) -> String;
fn bridge_is_portable(manager: &UpdateManagerOpaque) -> bool;
fn bridge_update_pending_restart(manager: &UpdateManagerOpaque) -> AssetOption;
fn bridge_check_for_updates(manager: &UpdateManagerOpaque) -> Result<UpdateInfoOption>;
fn bridge_download_updates(manager: &UpdateManagerOpaque, to_download: &UpdateInfoDto, progress: &DownloadCallbackManager) -> Result<()>;
fn bridge_wait_exit_then_apply_update(manager: &UpdateManagerOpaque, to_apply: &AssetDto, silent: bool, restart: bool, restart_args: &Vec<String>) -> Result<()>;
fn bridge_appbuilder_run(cb: &HookCallbackManager, custom_args: &StringArrayOption, locator: &LocatorConfigOption, auto_apply: bool);
fn bridge_update_pending_restart(manager: &UpdateManagerOpaque) -> VelopackAssetDtoOption;
fn bridge_check_for_updates(manager: &UpdateManagerOpaque) -> Result<UpdateInfoDtoOption>;
fn bridge_download_updates(
manager: &UpdateManagerOpaque,
to_download: &UpdateInfoDto,
progress: &DownloadCallbackManager,
) -> Result<()>;
fn bridge_wait_exit_then_apply_update(
manager: &UpdateManagerOpaque,
to_apply: &VelopackAssetDto,
silent: bool,
restart: bool,
restart_args: &Vec<String>,
) -> Result<()>;
fn bridge_appbuilder_run(
cb: &HookCallbackManager,
custom_args: &StringArrayOption,
locator: &VelopackLocatorConfigDtoOption,
auto_apply: bool,
);
unsafe fn bridge_set_logger_callback(cb: *mut LoggerCallbackManager);
}
}
@@ -110,79 +144,16 @@ struct UpdateManagerOpaque {
obj: UpdateManager,
}
fn to_locator_config(locator: &ffi::LocatorConfigDto) -> VelopackLocatorConfig {
VelopackLocatorConfig {
RootAppDir: PathBuf::from(locator.RootAppDir.clone()),
UpdateExePath: PathBuf::from(locator.UpdateExePath.clone()),
PackagesDir: PathBuf::from(locator.PackagesDir.clone()),
ManifestPath: PathBuf::from(locator.ManifestPath.clone()),
CurrentBinaryDir: PathBuf::from(locator.CurrentBinaryDir.clone()),
IsPortable: locator.IsPortable,
}
}
fn to_update_options(options: &ffi::UpdateOptionsDto) -> VelopackUpdateOptions {
let channel = options.ExplicitChannel.clone();
VelopackUpdateOptions {
AllowVersionDowngrade: options.AllowVersionDowngrade,
ExplicitChannel: if channel.is_empty() { None } else { Some(channel) },
}
}
fn from_asset(asset: &VelopackAsset) -> ffi::AssetDto {
ffi::AssetDto {
PackageId: asset.PackageId.clone(),
Version: asset.Version.clone(),
Type: asset.Type.clone(),
FileName: asset.FileName.clone(),
SHA1: asset.SHA1.clone(),
SHA256: asset.SHA256.clone(),
Size: asset.Size,
NotesMarkdown: asset.NotesMarkdown.clone(),
NotesHtml: asset.NotesHtml.clone(),
}
}
fn to_asset(asset: &ffi::AssetDto) -> VelopackAsset {
VelopackAsset {
PackageId: asset.PackageId.clone(),
Version: asset.Version.clone(),
Type: asset.Type.clone(),
FileName: asset.FileName.clone(),
SHA1: asset.SHA1.clone(),
SHA256: asset.SHA256.clone(),
Size: asset.Size,
NotesMarkdown: asset.NotesMarkdown.clone(),
NotesHtml: asset.NotesHtml.clone(),
}
}
fn from_update_info(info: &VelopackUpdateInfo) -> ffi::UpdateInfoDto {
ffi::UpdateInfoDto {
TargetFullRelease: from_asset(&info.TargetFullRelease),
IsDowngrade: info.IsDowngrade,
}
}
fn to_update_info(info: &ffi::UpdateInfoDto) -> VelopackUpdateInfo {
VelopackUpdateInfo {
TargetFullRelease: to_asset(&info.TargetFullRelease),
IsDowngrade: info.IsDowngrade,
}
}
fn bridge_new_update_manager(url_or_path: &String, options: &ffi::UpdateOptionsDto, locator: &ffi::LocatorConfigOption) -> Result<Box<UpdateManagerOpaque>> {
fn bridge_new_update_manager(
url_or_path: &String,
options: &ffi::UpdateOptionsDtoOption,
locator: &ffi::VelopackLocatorConfigDtoOption,
) -> Result<Box<UpdateManagerOpaque>> {
let source = sources::AutoSource::new(url_or_path);
let update_options = to_update_options(&options);
if locator.has_data {
let locator_config = to_locator_config(&locator.data);
let update_manager = UpdateManager::new(source, Some(update_options), Some(locator_config))?;
Ok(Box::new(UpdateManagerOpaque { obj: update_manager }))
} else {
let update_manager = UpdateManager::new(source, Some(update_options), None)?;
Ok(Box::new(UpdateManagerOpaque { obj: update_manager }))
}
let options = updateoptions_to_core_option(options);
let locator = velopacklocatorconfig_to_core_option(locator);
let update_manager = UpdateManager::new(source, options, locator)?;
Ok(Box::new(UpdateManagerOpaque { obj: update_manager }))
}
fn bridge_get_current_version(manager: &UpdateManagerOpaque) -> String {
@@ -197,29 +168,22 @@ fn bridge_is_portable(manager: &UpdateManagerOpaque) -> bool {
manager.obj.get_is_portable()
}
fn bridge_update_pending_restart(manager: &UpdateManagerOpaque) -> ffi::AssetOption {
if let Some(info) = manager.obj.get_update_pending_restart() {
let update = from_asset(&info);
ffi::AssetOption { data: update, has_data: true }
} else {
let default = ffi::AssetDto::default();
ffi::AssetOption { data: default, has_data: false }
}
fn bridge_update_pending_restart(manager: &UpdateManagerOpaque) -> ffi::VelopackAssetDtoOption {
let asset_opt = manager.obj.get_update_pending_restart();
velopackasset_to_bridge_option(&asset_opt)
}
fn bridge_check_for_updates(manager: &UpdateManagerOpaque) -> Result<ffi::UpdateInfoOption> {
if let UpdateCheck::UpdateAvailable(info) = manager.obj.check_for_updates()? {
let update = from_update_info(&info);
Ok(ffi::UpdateInfoOption { data: update, has_data: true })
} else {
let default = ffi::UpdateInfoDto::default();
Ok(ffi::UpdateInfoOption { data: default, has_data: false })
}
fn bridge_check_for_updates(manager: &UpdateManagerOpaque) -> Result<ffi::UpdateInfoDtoOption> {
let info_opt = if let UpdateCheck::UpdateAvailable(info) = manager.obj.check_for_updates()? { Some(info) } else { None };
Ok(updateinfo_to_bridge_option(&info_opt))
}
fn bridge_download_updates(manager: &UpdateManagerOpaque, to_download: &ffi::UpdateInfoDto, cb: &ffi::DownloadCallbackManager) -> Result<()> {
let info = to_update_info(&to_download);
fn bridge_download_updates(
manager: &UpdateManagerOpaque,
to_download: &ffi::UpdateInfoDto,
cb: &ffi::DownloadCallbackManager,
) -> Result<()> {
let info = updateinfo_to_core(&to_download);
let (progress_sender, progress_receiver) = std::sync::mpsc::channel::<i16>();
let (completion_sender, completion_receiver) = std::sync::mpsc::channel::<std::result::Result<(), VelopackError>>();
@@ -260,13 +224,24 @@ fn bridge_download_updates(manager: &UpdateManagerOpaque, to_download: &ffi::Upd
}
}
fn bridge_wait_exit_then_apply_update(manager: &UpdateManagerOpaque, to_apply: &ffi::AssetDto, silent: bool, restart: bool, restart_args: &Vec<String>) -> Result<()> {
let asset = to_asset(&to_apply);
fn bridge_wait_exit_then_apply_update(
manager: &UpdateManagerOpaque,
to_apply: &ffi::VelopackAssetDto,
silent: bool,
restart: bool,
restart_args: &Vec<String>,
) -> Result<()> {
let asset = velopackasset_to_core(&to_apply);
manager.obj.wait_exit_then_apply_updates(&asset, silent, restart, restart_args)?;
Ok(())
}
fn bridge_appbuilder_run(cb: &ffi::HookCallbackManager, custom_args: &ffi::StringArrayOption, locator: &ffi::LocatorConfigOption, auto_apply: bool) {
fn bridge_appbuilder_run(
cb: &ffi::HookCallbackManager,
custom_args: &ffi::StringArrayOption,
locator: &ffi::VelopackLocatorConfigDtoOption,
auto_apply: bool,
) {
let mut app = VelopackApp::build()
.set_auto_apply_on_startup(auto_apply)
.on_first_run(|v| cb.firstrun_hook(v.to_string()))
@@ -282,7 +257,8 @@ fn bridge_appbuilder_run(cb: &ffi::HookCallbackManager, custom_args: &ffi::Strin
}
if locator.has_data {
app = app.set_locator(to_locator_config(&locator.data));
let locator = velopacklocatorconfig_to_core(&locator.data);
app = app.set_locator(locator);
}
if custom_args.has_data {
@@ -314,7 +290,8 @@ impl Log for LoggerImpl {
Level::Info => "info",
Level::Debug => "debug",
Level::Trace => "trace",
}.to_string();
}
.to_string();
if let Some(cb) = get_logger() {
if let Some(cb) = unsafe { cb.as_mut() } {

162
src/lib-cpp/src/map.rs Normal file
View File

@@ -0,0 +1,162 @@
#![allow(dead_code)]
use std::path::PathBuf;
use velopack::locator::VelopackLocatorConfig;
use velopack::{UpdateInfo, UpdateOptions, VelopackAsset};
use crate::ffi::*;
fn pathbuf_to_core(dto: &String) -> PathBuf {
PathBuf::from(dto)
}
fn pathbuf_to_bridge(dto: &PathBuf) -> String {
dto.to_string_lossy().to_string()
}
fn string_to_core(dto: &String) -> String {
dto.clone()
}
fn string_to_bridge(dto: &String) -> String {
dto.clone()
}
fn bool_to_core(dto: &bool) -> bool {
*dto
}
fn bool_to_bridge(dto: &bool) -> bool {
*dto
}
fn u64_to_core(dto: &u64) -> u64 {
*dto
}
fn u64_to_bridge(dto: &u64) -> u64 {
*dto
}
// !! AUTO-GENERATED-START CORE_MAPPING
pub fn velopacklocatorconfig_to_core(dto: &VelopackLocatorConfigDto) -> VelopackLocatorConfig {
VelopackLocatorConfig {
RootAppDir: pathbuf_to_core(&dto.RootAppDir),
UpdateExePath: pathbuf_to_core(&dto.UpdateExePath),
PackagesDir: pathbuf_to_core(&dto.PackagesDir),
ManifestPath: pathbuf_to_core(&dto.ManifestPath),
CurrentBinaryDir: pathbuf_to_core(&dto.CurrentBinaryDir),
IsPortable: bool_to_core(&dto.IsPortable),
}
}
pub fn velopacklocatorconfig_to_bridge(dto: &VelopackLocatorConfig) -> VelopackLocatorConfigDto {
VelopackLocatorConfigDto {
RootAppDir: pathbuf_to_bridge(&dto.RootAppDir),
UpdateExePath: pathbuf_to_bridge(&dto.UpdateExePath),
PackagesDir: pathbuf_to_bridge(&dto.PackagesDir),
ManifestPath: pathbuf_to_bridge(&dto.ManifestPath),
CurrentBinaryDir: pathbuf_to_bridge(&dto.CurrentBinaryDir),
IsPortable: bool_to_bridge(&dto.IsPortable),
}
}
pub fn velopacklocatorconfig_to_core_option(dto: &VelopackLocatorConfigDtoOption) -> Option<VelopackLocatorConfig> {
if dto.has_data { Some(velopacklocatorconfig_to_core(&dto.data)) } else { None }
}
pub fn velopacklocatorconfig_to_bridge_option(dto: &Option<VelopackLocatorConfig>) -> VelopackLocatorConfigDtoOption {
match dto {
Some(dto) => VelopackLocatorConfigDtoOption { data: velopacklocatorconfig_to_bridge(dto), has_data: true },
None => VelopackLocatorConfigDtoOption { data: Default::default(), has_data: false },
}
}
pub fn velopackasset_to_core(dto: &VelopackAssetDto) -> VelopackAsset {
VelopackAsset {
PackageId: string_to_core(&dto.PackageId),
Version: string_to_core(&dto.Version),
Type: string_to_core(&dto.Type),
FileName: string_to_core(&dto.FileName),
SHA1: string_to_core(&dto.SHA1),
SHA256: string_to_core(&dto.SHA256),
Size: u64_to_core(&dto.Size),
NotesMarkdown: string_to_core(&dto.NotesMarkdown),
NotesHtml: string_to_core(&dto.NotesHtml),
}
}
pub fn velopackasset_to_bridge(dto: &VelopackAsset) -> VelopackAssetDto {
VelopackAssetDto {
PackageId: string_to_bridge(&dto.PackageId),
Version: string_to_bridge(&dto.Version),
Type: string_to_bridge(&dto.Type),
FileName: string_to_bridge(&dto.FileName),
SHA1: string_to_bridge(&dto.SHA1),
SHA256: string_to_bridge(&dto.SHA256),
Size: u64_to_bridge(&dto.Size),
NotesMarkdown: string_to_bridge(&dto.NotesMarkdown),
NotesHtml: string_to_bridge(&dto.NotesHtml),
}
}
pub fn velopackasset_to_core_option(dto: &VelopackAssetDtoOption) -> Option<VelopackAsset> {
if dto.has_data { Some(velopackasset_to_core(&dto.data)) } else { None }
}
pub fn velopackasset_to_bridge_option(dto: &Option<VelopackAsset>) -> VelopackAssetDtoOption {
match dto {
Some(dto) => VelopackAssetDtoOption { data: velopackasset_to_bridge(dto), has_data: true },
None => VelopackAssetDtoOption { data: Default::default(), has_data: false },
}
}
pub fn updateinfo_to_core(dto: &UpdateInfoDto) -> UpdateInfo {
UpdateInfo {
TargetFullRelease: velopackasset_to_core(&dto.TargetFullRelease),
IsDowngrade: bool_to_core(&dto.IsDowngrade),
}
}
pub fn updateinfo_to_bridge(dto: &UpdateInfo) -> UpdateInfoDto {
UpdateInfoDto {
TargetFullRelease: velopackasset_to_bridge(&dto.TargetFullRelease),
IsDowngrade: bool_to_bridge(&dto.IsDowngrade),
}
}
pub fn updateinfo_to_core_option(dto: &UpdateInfoDtoOption) -> Option<UpdateInfo> {
if dto.has_data { Some(updateinfo_to_core(&dto.data)) } else { None }
}
pub fn updateinfo_to_bridge_option(dto: &Option<UpdateInfo>) -> UpdateInfoDtoOption {
match dto {
Some(dto) => UpdateInfoDtoOption { data: updateinfo_to_bridge(dto), has_data: true },
None => UpdateInfoDtoOption { data: Default::default(), has_data: false },
}
}
pub fn updateoptions_to_core(dto: &UpdateOptionsDto) -> UpdateOptions {
UpdateOptions {
AllowVersionDowngrade: bool_to_core(&dto.AllowVersionDowngrade),
ExplicitChannel: if dto.ExplicitChannel.has_data { Some(string_to_core(&dto.ExplicitChannel.data)) } else { None },
}
}
pub fn updateoptions_to_bridge(dto: &UpdateOptions) -> UpdateOptionsDto {
UpdateOptionsDto {
AllowVersionDowngrade: bool_to_bridge(&dto.AllowVersionDowngrade),
ExplicitChannel: StringOption { data: string_to_bridge(&dto.ExplicitChannel.clone().unwrap_or_default()), has_data: dto.ExplicitChannel.is_some() },
}
}
pub fn updateoptions_to_core_option(dto: &UpdateOptionsDtoOption) -> Option<UpdateOptions> {
if dto.has_data { Some(updateoptions_to_core(&dto.data)) } else { None }
}
pub fn updateoptions_to_bridge_option(dto: &Option<UpdateOptions>) -> UpdateOptionsDtoOption {
match dto {
Some(dto) => UpdateOptionsDtoOption { data: updateoptions_to_bridge(dto), has_data: true },
None => UpdateOptionsDtoOption { data: Default::default(), has_data: false },
}
}
// !! AUTO-GENERATED-END CORE_MAPPING

View File

@@ -0,0 +1,52 @@
using System.Text;
public class IndentStringBuilder
{
private StringBuilder _sb = new();
int _indent = 0;
public void AppendLine()
{
_sb.AppendLine();
}
public void AppendLine(string text)
{
AppendIndent();
_sb.AppendLine(text);
}
private void AppendIndent()
{
_sb.Append(' ', _indent * 4);
}
public IDisposable Indent()
{
_indent++;
return new IndentDisposable(this);
}
private void RemoveIndent()
{
_indent--;
}
public override string ToString()
{
return _sb.ToString();
}
private class IndentDisposable(IndentStringBuilder isb) : IDisposable
{
private bool _disposed = false;
public void Dispose()
{
if (!_disposed) {
isb.RemoveIndent();
_disposed = true;
}
}
}
}

View File

@@ -0,0 +1,92 @@
using System.Reflection;
var scriptsDir = Assembly.GetEntryAssembly()!
.GetCustomAttributes<AssemblyMetadataAttribute>()
.Single(x => x.Key == "SelfDir").Value!;
var librustDir = Path.Combine(scriptsDir, "..", "..", "lib-rust", "src");
var libcppDir = Path.Combine(scriptsDir, "..");
var files = Directory.EnumerateFiles(librustDir, "*.rs", SearchOption.AllDirectories);
string[] desiredStructs = [
"VelopackAsset",
"UpdateInfo",
"UpdateOptions",
"VelopackLocatorConfig",
];
Dictionary<string, string> basic_libc_names = new() {
{ "VelopackAsset", "vpkc_asset_t" },
{ "UpdateInfo", "vpkc_update_info_t" },
{ "UpdateOptions", "vpkc_update_options_t" },
{ "VelopackLocatorConfig", "vpkc_locator_config_t" },
};
List<RustStruct> availableStructs = new();
string[] searchStrings = desiredStructs.Select(s => "struct " + s + " {").ToArray();
foreach (var file in files) {
Console.WriteLine(file);
var text = File.ReadAllText(file);
var structs = StructFinder.FindStructs(text);
foreach (var s in structs) {
if (searchStrings.Any(search => s.Contains(search))) {
var result = StructParser.ParseStructs(s);
availableStructs.AddRange(result);
}
}
}
if (desiredStructs.Length != availableStructs.Count) {
Console.WriteLine("Not all structs were found.");
Console.WriteLine("Desired structs: " + string.Join(", ", desiredStructs));
Console.WriteLine("Available structs: " + string.Join(", ", availableStructs.Select(s => s.Name)));
return -1;
}
// rust bridge code
string rustCppLib = Path.Combine(libcppDir, "src", "lib.rs");
string rustCppMap = Path.Combine(libcppDir, "src", "map.rs");
string rustCppInclude = Path.Combine(libcppDir, "include", "Velopack.h");
string rustBridgeC = Path.Combine(libcppDir, "src", "bridge.cc");
Console.WriteLine("Generating bridge dtos");
var sbBridgeDto = new IndentStringBuilder();
foreach(var rs in availableStructs) {
Templates.WriteBridgeDto(desiredStructs, sbBridgeDto, rs);
}
Console.WriteLine("Generating bridge to core mappings");
var sbBridgeMapping = new IndentStringBuilder();
foreach(var rs in availableStructs) {
Templates.WriteBridgeToCoreMapping(desiredStructs, sbBridgeMapping, rs);
}
Console.WriteLine("Generating C types");
var cTypes = new IndentStringBuilder();
foreach(var rs in availableStructs) {
Templates.WriteBasicC(basic_libc_names, cTypes, rs);
}
Console.WriteLine("Generating C++ types");
var cppTypes = new IndentStringBuilder();
foreach(var rs in availableStructs) {
Templates.WriteCPlusPlus(basic_libc_names, cppTypes, rs);
}
Console.WriteLine("Generating C to bridge mappings");
var cToBridgeMapping = new IndentStringBuilder();
foreach(var rs in availableStructs) {
Templates.WriteCBridgeMapping(basic_libc_names, cToBridgeMapping, rs);
}
Console.WriteLine("Writing all to file");
Util.ReplaceTextInFile(rustCppLib, "BRIDGE_DTOS", sbBridgeDto.ToString());
Util.ReplaceTextInFile(rustCppMap, "CORE_MAPPING", sbBridgeMapping.ToString());
Util.ReplaceTextInFile(rustCppInclude, "C_TYPES", cTypes.ToString());
Util.ReplaceTextInFile(rustCppInclude, "CPP_TYPES", cppTypes.ToString());
Util.ReplaceTextInFile(rustBridgeC, "BRIDGE_MAPPING", cToBridgeMapping.ToString());
return 0;

View File

@@ -0,0 +1,45 @@
using System.Text.RegularExpressions;
public static class StructFinder
{
public static string[] FindStructs(string code)
{
List<string> structs = new();
code = code.ReplaceLineEndings("\n");
foreach (var match in Regex.EnumerateMatches(code, @"(^|[^\S\r\n]+)(pub\s*)?struct")) {
var linesBefore = code.Substring(0, match.Index).Split(new char[] { '\n' }, StringSplitOptions.None);
var beginLine = linesBefore
.Select((Text, Index) => new { Text, Index })
.Last(p => String.IsNullOrWhiteSpace(p.Text))
.Index + 1;
var startIndex = linesBefore
.Take(beginLine)
.Aggregate(0, (i, s) => i + s.Length + 1);
string textAfterStart = code.Substring(startIndex);
int firstOpenBrace = textAfterStart.IndexOf('{');
int numOpenBraces = 1;
// Find the end of the struct by counting open and close braces until numOpenBraces == 0
for (int i = firstOpenBrace + 1; i < textAfterStart.Length; i++) {
if (textAfterStart[i] == '{') {
numOpenBraces++;
} else if (textAfterStart[i] == '}') {
numOpenBraces--;
}
if (numOpenBraces == 0) {
var end = startIndex + i + 1;
string text = code.Substring(startIndex, end - startIndex);
structs.Add(text);
break;
}
}
}
return structs.ToArray();
}
}

View File

@@ -0,0 +1,171 @@
using System.Text.RegularExpressions;
using Superpower;
using Superpower.Parsers;
using Superpower.Model;
using Superpower.Tokenizers;
public class RustField
{
public string DocComment { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public bool Optional { get; set; }
}
public class RustStruct
{
public string DocComment { get; set; }
public string Name { get; set; }
public List<RustField> Fields { get; set; }
}
public enum RustToken
{
DocComment,
Attribute,
KeywordPub,
KeywordStruct,
KeywordImpl,
Identifier,
OpenBrace, // {
CloseBrace, // }
Colon, // :
Semicolon, // ;
Comma, // ,
OtherSymbol,
}
public static class StructParser
{
// Tokenizer
private static readonly Tokenizer<RustToken> Tokenizer = new TokenizerBuilder<RustToken>()
.Ignore(Span.WhiteSpace)
.Ignore(Span.Regex(@"/\*[\s\S]*?\*/")) // Ignore multi-line comments
.Ignore(Span.Regex(@"(\s|^)\/\/[^\/].*")) // Ignore single-line comments but not doc comments
.Match(Span.Regex(@"///.*"), RustToken.DocComment)
.Match(Span.Regex(@"#\[[^\]]*\]"), RustToken.Attribute)
.Match(Span.EqualTo("pub"), RustToken.KeywordPub)
.Match(Span.EqualTo("struct"), RustToken.KeywordStruct)
.Match(Span.EqualTo("impl"), RustToken.KeywordImpl)
.Match(Span.Regex(@"[a-zA-Z_][a-zA-Z0-9_<>]*"), RustToken.Identifier)
.Match(Character.EqualTo('{'), RustToken.OpenBrace)
.Match(Character.EqualTo('}'), RustToken.CloseBrace)
.Match(Character.EqualTo(':'), RustToken.Colon)
.Match(Character.EqualTo(';'), RustToken.Semicolon)
.Match(Character.EqualTo(','), RustToken.Comma)
.Match(Character.AnyChar, RustToken.OtherSymbol)
.Ignore(Span.WhiteSpace)
.Build();
// Parsers
private static readonly TokenListParser<RustToken, string> DocComment =
Token.EqualTo(RustToken.DocComment)
.Many()
.Select(docs => string.Join("\n", docs.Select(doc =>
doc.ToStringValue().Substring(3).Trim())));
private static readonly TokenListParser<RustToken, Unit> Attribute =
Token.EqualTo(RustToken.Attribute).Many().Select(_ => Unit.Value);
private static TokenListParser<RustToken, Unit> SkipNestedBraces()
{
return
from open in Token.EqualTo(RustToken.OpenBrace)
from content in SkipNestedBracesContent()
from close in Token.EqualTo(RustToken.CloseBrace)
select Unit.Value;
}
private static TokenListParser<RustToken, Unit> SkipNestedBracesContent()
{
return
(from nested in SkipNestedBraces() select Unit.Value) // handle recursive braces
.Or(from nonBrace in Token.Matching<RustToken>(kind => kind != RustToken.OpenBrace && kind != RustToken.CloseBrace,"non-brace").AtLeastOnce() select Unit.Value).Many()
.Select(_ => Unit.Value);
}
private static readonly TokenListParser<RustToken, Unit> ImplBlock =
from implKeyword in Token.EqualTo(RustToken.KeywordImpl)
from rest in Token.Matching<RustToken>(kind => kind != RustToken.OpenBrace, "Expected tokens before '{'").AtLeastOnce()
from content in SkipNestedBraces()
select Unit.Value;
private static readonly TokenListParser<RustToken, string> TypeParser =
from rest in Token.Matching<RustToken>(kind => kind != RustToken.Comma, "Expected tokens before ','").AtLeastOnce()
from end in Token.EqualTo(RustToken.Comma)
select string.Join(" ", rest.Select(t => t.ToStringValue()));
private static readonly TokenListParser<RustToken, RustField> FieldDefinition =
from attrs1 in Attribute.Optional()
from docComments in DocComment.OptionalOrDefault()
from attrs2 in Attribute.Optional()
from pub in Token.EqualTo(RustToken.KeywordPub).Optional()
from fieldName in Token.EqualTo(RustToken.Identifier).Select(t => t.ToStringValue())
from colon in Token.EqualTo(RustToken.Colon)
from fieldType in TypeParser
select new RustField
{
DocComment = docComments,
Name = fieldName,
Type = fieldType.Trim()
};
private static readonly TokenListParser<RustToken, List<RustField>> StructBody =
from openBrace in Token.EqualTo(RustToken.OpenBrace)
from fields in FieldDefinition.Many()
from closeBrace in Token.EqualTo(RustToken.CloseBrace)
select fields.ToList();
private static readonly TokenListParser<RustToken, RustStruct> StructDefinition =
from attrs1 in Attribute.Optional()
from docComments in DocComment.OptionalOrDefault()
from attrs2 in Attribute.Optional()
from pub in Token.EqualTo(RustToken.KeywordPub).Optional()
from structKeyword in Token.EqualTo(RustToken.KeywordStruct)
from structName in Token.EqualTo(RustToken.Identifier).Select(t => t.ToStringValue())
from structBody in StructBody
select new RustStruct
{
DocComment = docComments,
Name = structName,
Fields = structBody
};
private static readonly TokenListParser<RustToken, RustStruct> TopLevelItem =
(from impl in ImplBlock
select (RustStruct)null)
.Or(
from structDef in StructDefinition
select structDef
);
public static IEnumerable<RustStruct> ParseStructs(string code)
{
var tokens = Tokenizer.Tokenize(code);
var parser = TopLevelItem.Many();
var result = parser(tokens);
if (!result.HasValue)
{
throw new Exception(result.ToString());
}
var structs = result.Value.Where(s => s != null).ToArray();
foreach(var s in structs)
{
foreach(var f in s.Fields)
{
var match = Regex.Match(f.Type, @"Option<(.*)>");
// If the field type is an Option, extract the inner type and set Optional to true
if (match.Success)
{
f.Type = match.Groups[1].Value;
f.Optional = true;
}
}
}
return structs;
}
}

View File

@@ -0,0 +1,294 @@
using System.Text;
public static class Templates
{
private static string GetBasicCType(Dictionary<string, string> nameMap, string rustType)
{
switch (rustType) {
case "PathBuf":
case "String":
return "char*";
case "bool":
return "bool";
case "i32":
return "int64_t";
case "i64":
return "int64_t";
case "u32":
return "uint32_t";
case "u64":
return "uint64_t";
default:
if (nameMap.TryGetValue(rustType, out var type)) {
return type;
}
throw new NotSupportedException("Unsupported type for basic-c: " + rustType);
}
}
private static string GetCPlusPlusType(string[] coreTypes, string rustType, bool optional)
{
string type = rustType switch {
"PathBuf" => "std::string",
"String" => "std::string",
"bool" => "bool",
"i32" => "int64_t",
"i64" => "int64_t",
"u32" => "uint32_t",
"u64" => "uint64_t",
_ => coreTypes.Contains(rustType) ? rustType : throw new NotSupportedException("Unsupported type for c-plus-plus: " + rustType),
};
return optional ? "std::optional<" + type + ">" : type;
}
public static void WriteCBridgeMapping(Dictionary<string, string> nameMap, IndentStringBuilder sb, RustStruct rs)
{
var cName = nameMap[rs.Name];
sb.AppendLine($"static inline {rs.Name}Dto to_bridge({cName}* pDto) {{");
using (sb.Indent()) {
sb.AppendLine($"if (pDto == nullptr) {{ return {{}}; }}");
sb.AppendLine($"return {{");
using (sb.Indent()) {
foreach (var field in rs.Fields) {
string suffix = field.Optional ? "_opt" : "";
string type = field.Type == "PathBuf" ? "string" : field.Type.ToLower();
if (nameMap.ContainsKey(field.Type)) {
sb.AppendLine($"to_bridge{suffix}(&pDto->{field.Name}),");
} else if (type == "string") {
sb.AppendLine($"to_bridge{type}{suffix}(pDto->{field.Name}),");
} else {
sb.AppendLine($"pDto->{field.Name},");
}
}
}
sb.AppendLine("};");
}
sb.AppendLine($"}}");
sb.AppendLine();
sb.AppendLine($"static inline {rs.Name}DtoOption to_bridge_opt({cName}* pDto) {{");
using (sb.Indent()) {
sb.AppendLine($"{rs.Name}DtoOption opt;");
sb.AppendLine($"if (pDto == nullptr) {{");
using (sb.Indent()) {
sb.AppendLine($"opt.has_data = false;");
sb.AppendLine($"return opt;");
}
sb.AppendLine($"}}");
sb.AppendLine();
sb.AppendLine($"opt.has_data = true;");
sb.AppendLine($"opt.data = to_bridge(pDto);");
sb.AppendLine($"return opt;");
}
sb.AppendLine($"}}");
sb.AppendLine();
sb.AppendLine($"static inline void allocate_{rs.Name.ToLower()}({rs.Name}Dto bridgeDto, {cName}* pDto) {{");
using (sb.Indent()) {
sb.AppendLine($"if (pDto == nullptr) {{ return; }}");
foreach (var field in rs.Fields) {
string type = field.Type == "PathBuf" ? "string" : field.Type.ToLower();
string suffix = field.Optional ? "_opt" : "";
sb.AppendLine(
nameMap.ContainsKey(field.Type) || type == "string"
? $"allocate_{type}{suffix}(bridgeDto.{field.Name}, &pDto->{field.Name});"
: $"pDto->{field.Name} = bridgeDto.{field.Name};");
}
}
sb.AppendLine($"}}");
sb.AppendLine();
sb.AppendLine($"static inline void free_{rs.Name.ToLower()}({cName}* pDto) {{");
using (sb.Indent()) {
sb.AppendLine($"if (pDto == nullptr) {{ return; }}");
foreach (var field in rs.Fields) {
string type = field.Type == "PathBuf" ? "string" : field.Type.ToLower();
if (nameMap.ContainsKey(field.Type)) {
sb.AppendLine($"free_{type}(&pDto->{field.Name});");
} else if (type == "string") {
sb.AppendLine($"free(pDto->{field.Name});");
}
}
}
sb.AppendLine($"}}");
sb.AppendLine();
}
public static void WriteBasicC(Dictionary<string, string> nameMap, IndentStringBuilder sb, RustStruct rs)
{
sb.AppendLine($"typedef struct {{");
foreach (var field in rs.Fields) {
sb.AppendLine($" {GetBasicCType(nameMap, field.Type)} {field.Name};");
}
sb.AppendLine($"}} {nameMap[rs.Name]};");
sb.AppendLine();
}
public static void WriteCPlusPlus(Dictionary<string, string> nameMap, IndentStringBuilder sb, RustStruct rs)
{
var coreTypes = nameMap.Keys.ToArray();
sb.AppendLine($"struct {rs.Name} {{");
foreach (var field in rs.Fields) {
sb.AppendLine($" {GetCPlusPlusType(coreTypes, field.Type, field.Optional)} {field.Name};");
}
sb.AppendLine($"}};");
sb.AppendLine();
sb.AppendLine($"static inline {nameMap[rs.Name]} to_c(const {rs.Name}& dto) {{");
using (sb.Indent()) {
sb.AppendLine("return {");
using (sb.Indent()) {
foreach (var field in rs.Fields) {
string suffix = field.Optional ? "_opt" : "";
string type = field.Type == "PathBuf" ? "string" : field.Type.ToLower();
sb.AppendLine(
nameMap.ContainsKey(field.Type)
? $"to_c{suffix}(dto.{field.Name}),"
: $"to_c{type}{suffix}(dto.{field.Name}),");
}
}
sb.AppendLine("};");
}
sb.AppendLine($"}}");
sb.AppendLine();
sb.AppendLine($"static inline {rs.Name} to_cpp(const {nameMap[rs.Name]}& dto) {{");
using (sb.Indent()) {
sb.AppendLine("return {");
using (sb.Indent()) {
foreach (var field in rs.Fields) {
string suffix = field.Optional ? "_opt" : "";
string type = field.Type == "PathBuf" ? "string" : field.Type.ToLower();
sb.AppendLine(
nameMap.ContainsKey(field.Type)
? $"to_cpp{suffix}(dto.{field.Name}),"
: $"to_cpp{type}{suffix}(dto.{field.Name}),");
}
}
sb.AppendLine("};");
}
sb.AppendLine($"}}");
sb.AppendLine();
}
public static void WriteBridgeDto(string[] coreTypes, IndentStringBuilder sb, RustStruct rs)
{
Func<string, string> nameMapper = (str) =>
coreTypes.Contains(str) ? str + "Dto" : str;
using (sb.Indent()) {
sb.AppendLine($"#[derive(Default)]");
sb.AppendLine($"pub struct {nameMapper(rs.Name)} {{");
foreach (var field in rs.Fields) {
string type = field.Type;
if (type == "PathBuf") {
type = "String";
}
using (sb.Indent()) {
if (field.Optional) {
sb.AppendLine($"pub {field.Name}: {nameMapper(type)}Option,");
} else {
sb.AppendLine($"pub {field.Name}: {nameMapper(type)},");
}
}
}
sb.AppendLine($"}}");
sb.AppendLine();
sb.AppendLine($"#[derive(Default)]");
sb.AppendLine($"pub struct {nameMapper(rs.Name)}Option {{");
using (sb.Indent()) {
sb.AppendLine($"pub data: {nameMapper(rs.Name)},");
sb.AppendLine($"pub has_data: bool,");
}
sb.AppendLine($"}}");
sb.AppendLine();
}
}
public static void WriteBridgeToCoreMapping(string[] coreTypes, IndentStringBuilder sb, RustStruct rs)
{
Func<string, string> nameMapper = (str) => coreTypes.Contains(str) ? str + "Dto" : str;
sb.AppendLine($"pub fn {rs.Name.ToLower()}_to_core(dto: &{nameMapper(rs.Name)}) -> {rs.Name} {{");
;
using (sb.Indent()) {
sb.AppendLine($"{rs.Name} {{");
foreach (var field in rs.Fields) {
using (sb.Indent()) {
if (field.Optional) {
sb.AppendLine(
$"{field.Name}: if dto.{field.Name}.has_data {{ Some({field.Type.ToLower()}_to_core(&dto.{field.Name}.data)) }} else {{ None }},");
} else {
sb.AppendLine($"{field.Name}: {field.Type.ToLower()}_to_core(&dto.{field.Name}),");
}
}
}
sb.AppendLine($"}}");
}
sb.AppendLine($"}}");
sb.AppendLine();
sb.AppendLine($"pub fn {rs.Name.ToLower()}_to_bridge(dto: &{rs.Name}) -> {nameMapper(rs.Name)} {{");
using (sb.Indent()) {
sb.AppendLine($"{nameMapper(rs.Name)} {{");
foreach (var field in rs.Fields) {
using (sb.Indent()) {
if (field.Optional) {
sb.AppendLine(
$"{field.Name}: {nameMapper(field.Type)}Option {{ data: {field.Type.ToLower()}_to_bridge(&dto.{field.Name}.clone().unwrap_or_default()), has_data: dto.{field.Name}.is_some() }},");
} else {
sb.AppendLine($"{field.Name}: {field.Type.ToLower()}_to_bridge(&dto.{field.Name}),");
}
}
}
sb.AppendLine($"}}");
}
sb.AppendLine($"}}");
sb.AppendLine();
sb.AppendLine($"pub fn {rs.Name.ToLower()}_to_core_option(dto: &{nameMapper(rs.Name)}Option) -> Option<{rs.Name}> {{");
;
using (sb.Indent()) {
sb.AppendLine($"if dto.has_data {{ Some({rs.Name.ToLower()}_to_core(&dto.data)) }} else {{ None }}");
}
sb.AppendLine($"}}");
sb.AppendLine();
sb.AppendLine($"pub fn {rs.Name.ToLower()}_to_bridge_option(dto: &Option<{rs.Name}>) -> {nameMapper(rs.Name)}Option {{");
;
using (sb.Indent()) {
sb.AppendLine($"match dto {{");
using (sb.Indent()) {
sb.AppendLine($"Some(dto) => {nameMapper(rs.Name)}Option {{ data: {rs.Name.ToLower()}_to_bridge(dto), has_data: true }},");
sb.AppendLine($"None => {nameMapper(rs.Name)}Option {{ data: Default::default(), has_data: false }},");
}
sb.AppendLine($"}}");
}
sb.AppendLine($"}}");
sb.AppendLine();
}
}

View File

@@ -0,0 +1,27 @@
public static class Util
{
public static void ReplaceTextInFile(string path, string placeholderName, string text)
{
var body = File.ReadAllText(path);
ReplaceTextBetween(ref body, placeholderName, text);
File.WriteAllText(path, body);
}
public static void ReplaceTextBetween(ref string body, string placeholderName, string text)
{
var start = $"// !! AUTO-GENERATED-START {placeholderName}";
var end = $"// !! AUTO-GENERATED-END {placeholderName}";
var startIndex = body.IndexOf(start);
var endIndex = body.IndexOf(end);
if (startIndex == -1 || endIndex == -1) {
throw new InvalidOperationException($"Could not find placeholder {placeholderName}");
}
// normalize the start index to the beginning of the next line and end index to the end of the previous line
startIndex = body.IndexOf('\n', startIndex) + 1;
endIndex = body.LastIndexOf('\n', endIndex);
body = body.Remove(startIndex, endIndex - startIndex);
body = body.Insert(startIndex, text.TrimEnd());
}
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Superpower" Version="3.0.0" />
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
<_Parameter1>SelfDir</_Parameter1>
<_Parameter2>$(MSBuildThisFileDirectory)</_Parameter2>
</AssemblyAttribute>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "type-generator", "type-generator.csproj", "{1C3B3A9D-0D1C-44BF-9004-FB973820932C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1C3B3A9D-0D1C-44BF-9004-FB973820932C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1C3B3A9D-0D1C-44BF-9004-FB973820932C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C3B3A9D-0D1C-44BF-9004-FB973820932C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C3B3A9D-0D1C-44BF-9004-FB973820932C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal