mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Add new type mapping generator
This commit is contained in:
@@ -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,6 +128,67 @@ VPKC_EXPORT void VPKC_CALL vpkc_free_asset(vpkc_asset_t* pAsset);
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace Velopack {
|
||||
|
||||
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 std::string to_cppstring(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 std::optional<std::string> to_cppstring_opt(const char* psz) {
|
||||
return psz == nullptr ? std::nullopt : std::optional<std::string>(psz);
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
// !! 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 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;
|
||||
@@ -138,91 +201,72 @@ namespace Velopack {
|
||||
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::string ExplicitChannel;
|
||||
std::optional<std::string> ExplicitChannel;
|
||||
};
|
||||
|
||||
struct VelopackLocator {
|
||||
std::string RootAppDir;
|
||||
std::string UpdateExePath;
|
||||
std::string PackagesDir;
|
||||
std::string ManifestPath;
|
||||
std::string CurrentBinaryDir;
|
||||
bool IsPortable;
|
||||
};
|
||||
|
||||
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 vpkc_locator_t to_vpkc(const VelopackLocator& locator) {
|
||||
static inline vpkc_update_options_t to_c(const UpdateOptions& dto) {
|
||||
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
|
||||
to_cbool(dto.AllowVersionDowngrade),
|
||||
to_cstring_opt(dto.ExplicitChannel),
|
||||
};
|
||||
}
|
||||
|
||||
static inline vpkc_options_t to_vpkc(const UpdateOptions& options) {
|
||||
static inline UpdateOptions to_cpp(const vpkc_update_options_t& dto) {
|
||||
return {
|
||||
options.AllowVersionDowngrade,
|
||||
const_cast<char*>(options.ExplicitChannel.c_str())
|
||||
};
|
||||
}
|
||||
|
||||
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())
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
to_cppbool(dto.AllowVersionDowngrade),
|
||||
to_cppstring_opt(dto.ExplicitChannel),
|
||||
};
|
||||
}
|
||||
// !! AUTO-GENERATED-END CPP_TYPES
|
||||
|
||||
class VelopackApp {
|
||||
private:
|
||||
@@ -250,9 +294,9 @@ namespace Velopack {
|
||||
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));
|
||||
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) {
|
||||
@@ -288,17 +332,17 @@ namespace Velopack {
|
||||
private:
|
||||
vpkc_update_manager_t m_pManager;
|
||||
public:
|
||||
UpdateManager(const std::string& urlOrPath, const UpdateOptions* options, const VelopackLocator* locator) {
|
||||
vpkc_options_t* pOptions = nullptr;
|
||||
UpdateManager(const std::string& urlOrPath, const UpdateOptions* options = nullptr, const VelopackLocatorConfig* locator = nullptr) {
|
||||
vpkc_update_options_t* pOptions = nullptr;
|
||||
if (options != nullptr) {
|
||||
vpkc_options_t vpkc_options = to_vpkc(*options);
|
||||
pOptions = const_cast<vpkc_options_t*>(&vpkc_options);
|
||||
vpkc_update_options_t vpkc_options = to_c(*options);
|
||||
pOptions = &vpkc_options;
|
||||
}
|
||||
|
||||
vpkc_locator_t* pLocator = nullptr;
|
||||
vpkc_locator_config_t* pLocator = nullptr;
|
||||
if (locator != nullptr) {
|
||||
vpkc_locator_t vpkc_locator = to_vpkc(*locator);
|
||||
pLocator = const_cast<vpkc_locator_t*>(&vpkc_locator);
|
||||
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)) {
|
||||
@@ -326,7 +370,7 @@ namespace Velopack {
|
||||
std::optional<VelopackAsset> UpdatePendingRestart() noexcept {
|
||||
vpkc_asset_t asset;
|
||||
if (vpkc_update_pending_restart(&m_pManager, &asset)) {
|
||||
VelopackAsset cpp_asset = from_vpkc(asset);
|
||||
VelopackAsset cpp_asset = to_cpp(asset);
|
||||
vpkc_free_asset(&asset);
|
||||
return cpp_asset;
|
||||
}
|
||||
@@ -342,13 +386,13 @@ namespace Velopack {
|
||||
case vpkc_update_check_t::NO_UPDATE_AVAILABLE:
|
||||
return std::nullopt;
|
||||
case vpkc_update_check_t::UPDATE_AVAILABLE:
|
||||
UpdateInfo cpp_info = from_vpkc(update);
|
||||
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_vpkc(update);
|
||||
vpkc_update_info_t vpkc_update = to_c(update);
|
||||
if (!vpkc_download_updates(&m_pManager, &vpkc_update, progress)) {
|
||||
throw_last_error();
|
||||
}
|
||||
@@ -360,7 +404,7 @@ namespace Velopack {
|
||||
strcpy_s(pRestartArgs[i], restartArgs[i].size() + 1, restartArgs[i].c_str());
|
||||
}
|
||||
|
||||
vpkc_asset_t vpkc_asset = to_vpkc(asset);
|
||||
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
|
||||
@@ -377,7 +421,8 @@ namespace Velopack {
|
||||
this->WaitExitThenApplyUpdate(asset.TargetFullRelease, silent, restart, restartArgs);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Velopack
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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))?;
|
||||
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 }))
|
||||
} else {
|
||||
let update_manager = UpdateManager::new(source, Some(update_options), None)?;
|
||||
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
162
src/lib-cpp/src/map.rs
Normal 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
|
||||
52
src/lib-cpp/type-generator/IndentStringBuilder.cs
Normal file
52
src/lib-cpp/type-generator/IndentStringBuilder.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
src/lib-cpp/type-generator/Program.cs
Normal file
92
src/lib-cpp/type-generator/Program.cs
Normal 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;
|
||||
45
src/lib-cpp/type-generator/StructFinder.cs
Normal file
45
src/lib-cpp/type-generator/StructFinder.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
171
src/lib-cpp/type-generator/StructParser.cs
Normal file
171
src/lib-cpp/type-generator/StructParser.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
294
src/lib-cpp/type-generator/Templates.cs
Normal file
294
src/lib-cpp/type-generator/Templates.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
27
src/lib-cpp/type-generator/Util.cs
Normal file
27
src/lib-cpp/type-generator/Util.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
21
src/lib-cpp/type-generator/type-generator.csproj
Normal file
21
src/lib-cpp/type-generator/type-generator.csproj
Normal 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>
|
||||
16
src/lib-cpp/type-generator/type-generator.sln
Normal file
16
src/lib-cpp/type-generator/type-generator.sln
Normal 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
|
||||
Reference in New Issue
Block a user