mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
wip cpp template
This commit is contained in:
@@ -55,7 +55,7 @@ typedef struct vpkc_asset_t {
|
||||
*/
|
||||
char *Version;
|
||||
/**
|
||||
* The type of asset (eg. "Full" or "Delta").
|
||||
* The type of asset (eg. "Full" or "Delta").
|
||||
*/
|
||||
char *Type;
|
||||
/**
|
||||
|
||||
@@ -19,194 +19,27 @@
|
||||
|
||||
namespace Velopack {
|
||||
|
||||
static inline void throw_last_error() {
|
||||
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 std::optional<std::string> to_cpp_string(const char* psz)
|
||||
{
|
||||
return psz == nullptr ? std::optional<std::string>("") : std::optional<std::string>(psz);
|
||||
}
|
||||
|
||||
static inline char* to_cstring(const std::string& str) {
|
||||
return const_cast<char*>(str.c_str());
|
||||
static inline char* alloc_c_string(const std::optional<std::string>& str)
|
||||
{
|
||||
if (!str.has_value()) { return nullptr; }
|
||||
return alloc_c_string(str.value());
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
/// VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth).
|
||||
struct VelopackLocatorConfig {
|
||||
/// The root directory of the current app.
|
||||
std::string RootAppDir;
|
||||
/// The path to the Update.exe binary.
|
||||
std::string UpdateExePath;
|
||||
/// The path to the packages' directory.
|
||||
std::string PackagesDir;
|
||||
/// The current app manifest.
|
||||
std::string ManifestPath;
|
||||
/// The directory containing the application's user binaries.
|
||||
std::string CurrentBinaryDir;
|
||||
/// Whether the current application is portable or installed.
|
||||
bool IsPortable;
|
||||
};
|
||||
|
||||
/// An individual Velopack asset, could refer to an asset on-disk or in a remote package feed.
|
||||
struct VelopackAsset {
|
||||
/// The name or Id of the package containing this release.
|
||||
std::string PackageId;
|
||||
/// The version of this release.
|
||||
std::string Version;
|
||||
/// The type of asset (eg. "Full" or "Delta").
|
||||
std::string Type;
|
||||
/// The filename of the update package containing this release.
|
||||
std::string FileName;
|
||||
/// The SHA1 checksum of the update package containing this release.
|
||||
std::string SHA1;
|
||||
/// The SHA256 checksum of the update package containing this release.
|
||||
std::string SHA256;
|
||||
/// The size in bytes of the update package containing this release.
|
||||
uint64_t Size;
|
||||
/// The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string.
|
||||
std::string NotesMarkdown;
|
||||
/// The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string.
|
||||
std::string NotesHtml;
|
||||
};
|
||||
|
||||
/// Holds information about the current version and pending updates, such as how many there are, and access to release notes.
|
||||
struct UpdateInfo {
|
||||
/// The available version that we are updating to.
|
||||
VelopackAsset TargetFullRelease;
|
||||
/// The base release that this update is based on. This is only available if the update is a delta update.
|
||||
std::optional<VelopackAsset> BaseRelease;
|
||||
/// The list of delta updates that can be applied to the base version to get to the target version.
|
||||
std::vector<VelopackAsset> DeltasToTarget;
|
||||
/// True if the update is a version downgrade or lateral move (such as when switching channels to the same version number).
|
||||
/// In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be
|
||||
/// deleted.
|
||||
bool IsDowngrade;
|
||||
};
|
||||
|
||||
/// Options to customise the behaviour of UpdateManager.
|
||||
struct UpdateOptions {
|
||||
/// Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading).
|
||||
/// This could happen if a release has bugs and was retracted from the release feed, or if you're using
|
||||
/// ExplicitChannel to switch channels to another channel where the latest version on that
|
||||
/// channel is lower than the current version.
|
||||
bool AllowVersionDowngrade;
|
||||
/// **This option should usually be left None**.
|
||||
/// Overrides the default channel used to fetch updates.
|
||||
/// The default channel will be whatever channel was specified on the command line when building this release.
|
||||
/// For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'.
|
||||
/// This allows users to automatically receive updates from the same channel they installed from. This options
|
||||
/// allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel
|
||||
/// without having to reinstall the application.
|
||||
std::optional<std::string> ExplicitChannel;
|
||||
/// Sets the maximum number of deltas to consider before falling back to a full update.
|
||||
/// The default is 10. Set to a negative number (eg. -1) to disable deltas.
|
||||
int64_t MaximumDeltasBeforeFallback;
|
||||
};
|
||||
|
||||
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),
|
||||
};
|
||||
}
|
||||
|
||||
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),
|
||||
};
|
||||
}
|
||||
|
||||
static inline vpkc_update_info_t to_c(const UpdateInfo& dto) {
|
||||
return {
|
||||
to_c(dto.TargetFullRelease),
|
||||
to_c_opt(dto.BaseRelease),
|
||||
to_c(dto.DeltasToTarget),
|
||||
to_cbool(dto.IsDowngrade),
|
||||
};
|
||||
}
|
||||
|
||||
static inline UpdateInfo to_cpp(const vpkc_update_info_t& dto) {
|
||||
return {
|
||||
to_cpp(dto.TargetFullRelease),
|
||||
to_cpp_opt(dto.BaseRelease),
|
||||
to_cpp(dto.DeltasToTarget),
|
||||
to_cppbool(dto.IsDowngrade),
|
||||
};
|
||||
}
|
||||
|
||||
static inline vpkc_update_options_t to_c(const UpdateOptions& dto) {
|
||||
return {
|
||||
to_cbool(dto.AllowVersionDowngrade),
|
||||
to_cstring_opt(dto.ExplicitChannel),
|
||||
to_ci32(dto.MaximumDeltasBeforeFallback),
|
||||
};
|
||||
}
|
||||
|
||||
static inline UpdateOptions to_cpp(const vpkc_update_options_t& dto) {
|
||||
return {
|
||||
to_cppbool(dto.AllowVersionDowngrade),
|
||||
to_cppstring_opt(dto.ExplicitChannel),
|
||||
to_cppi32(dto.MaximumDeltasBeforeFallback),
|
||||
};
|
||||
}
|
||||
// !! AUTO-GENERATED-END CPP_TYPES
|
||||
|
||||
static inline char* allocate_cstring(const std::string& str) {
|
||||
static inline char* alloc_c_string(const std::string& str)
|
||||
{
|
||||
char* result = new char[str.size() + 1]; // +1 for null-terminator
|
||||
#ifdef _WIN32
|
||||
strcpy_s(result, str.size() + 1, str.c_str()); // Copy string content
|
||||
@@ -217,26 +50,403 @@ static inline char* allocate_cstring(const std::string& str) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void free_cstring(char* str) {
|
||||
static inline void free_c_string(char* str)
|
||||
{
|
||||
delete[] str;
|
||||
}
|
||||
|
||||
static inline char** allocate_cstring_array(const std::vector<std::string>& vec) {
|
||||
char** result = new char*[vec.size()];
|
||||
for (size_t i = 0; i < vec.size(); ++i) {
|
||||
result[i] = allocate_cstring(vec[i]);
|
||||
static inline char** alloc_c_string_vec(const std::vector<std::string>& dto, size_t* count)
|
||||
{
|
||||
if (dto.empty()) {
|
||||
*count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
return result;
|
||||
*count = dto.size();
|
||||
char** arr = new char* [*count];
|
||||
for (size_t i = 0; i < *count; ++i) {
|
||||
arr[i] = alloc_c_string(dto[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
static inline void free_cstring_array(char** arr, size_t size) {
|
||||
static inline void free_c_string_vec(char** arr, size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
free_cstring(arr[i]);
|
||||
free_c_string(arr[i]);
|
||||
arr[i] = nullptr;
|
||||
}
|
||||
delete[] arr;
|
||||
}
|
||||
|
||||
// !! AUTO-GENERATED-START CPP_TYPES
|
||||
|
||||
/** VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). */
|
||||
struct VelopackLocatorConfig {
|
||||
/** The root directory of the current app. */
|
||||
std::string RootAppDir;
|
||||
/** The path to the Update.exe binary. */
|
||||
std::string UpdateExePath;
|
||||
/** The path to the packages' directory. */
|
||||
std::string PackagesDir;
|
||||
/** The current app manifest. */
|
||||
std::string ManifestPath;
|
||||
/** The directory containing the application's user binaries. */
|
||||
std::string CurrentBinaryDir;
|
||||
/** Whether the current application is portable or installed. */
|
||||
bool IsPortable;
|
||||
};
|
||||
|
||||
static inline std::optional<VelopackLocatorConfig> to_cpp_VelopackLocatorConfig(const vpkc_locator_config_t* dto) {
|
||||
if (dto == nullptr) { return std::nullopt; }
|
||||
return std::optional<VelopackLocatorConfig>({
|
||||
to_cpp_string(dto->RootAppDir).value(),
|
||||
to_cpp_string(dto->UpdateExePath).value(),
|
||||
to_cpp_string(dto->PackagesDir).value(),
|
||||
to_cpp_string(dto->ManifestPath).value(),
|
||||
to_cpp_string(dto->CurrentBinaryDir).value(),
|
||||
dto->IsPortable,
|
||||
});
|
||||
}
|
||||
|
||||
static inline std::vector<VelopackLocatorConfig> to_cpp_VelopackLocatorConfig_vec(const vpkc_locator_config_t* const* arr, size_t c) {
|
||||
if (arr == nullptr || c < 1) { return std::vector<VelopackLocatorConfig>(); }
|
||||
std::vector<VelopackLocatorConfig> result;
|
||||
result.reserve(c);
|
||||
for (size_t i = 0; i < c; ++i) {
|
||||
auto dto = arr[i];
|
||||
if (dto == nullptr) { continue; }
|
||||
result.push_back(to_cpp_VelopackLocatorConfig(dto).value());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline vpkc_locator_config_t* alloc_c_VelopackLocatorConfig(const VelopackLocatorConfig* dto) {
|
||||
if (dto == nullptr) { return nullptr; }
|
||||
vpkc_locator_config_t* obj = new vpkc_locator_config_t{};
|
||||
obj->RootAppDir = alloc_c_string(dto->RootAppDir);
|
||||
obj->UpdateExePath = alloc_c_string(dto->UpdateExePath);
|
||||
obj->PackagesDir = alloc_c_string(dto->PackagesDir);
|
||||
obj->ManifestPath = alloc_c_string(dto->ManifestPath);
|
||||
obj->CurrentBinaryDir = alloc_c_string(dto->CurrentBinaryDir);
|
||||
obj->IsPortable = dto->IsPortable;
|
||||
return obj;
|
||||
}
|
||||
|
||||
static inline vpkc_locator_config_t* alloc_c_VelopackLocatorConfig(const std::optional<VelopackLocatorConfig>& dto) {
|
||||
if (!dto.has_value()) { return nullptr; }
|
||||
return alloc_c_VelopackLocatorConfig(dto.value());
|
||||
}
|
||||
|
||||
static inline vpkc_locator_config_t** alloc_c_VelopackLocatorConfig_vec(const std::vector<VelopackLocatorConfig>& dto, size_t* count) {
|
||||
if (dto.empty()) {
|
||||
*count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
*count = dto.size();
|
||||
vpkc_locator_config_t** arr = new vpkc_locator_config_t*[*count];
|
||||
for (size_t i = 0; i < *count; ++i) {
|
||||
arr[i] = alloc_c_VelopackLocatorConfig(dto[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
static inline void free_c_VelopackLocatorConfig(vpkc_locator_config_t* obj) {
|
||||
if (obj == nullptr) { return; }
|
||||
free_c_string(obj->RootAppDir);
|
||||
free_c_string(obj->UpdateExePath);
|
||||
free_c_string(obj->PackagesDir);
|
||||
free_c_string(obj->ManifestPath);
|
||||
free_c_string(obj->CurrentBinaryDir);
|
||||
|
||||
delete obj;
|
||||
}
|
||||
|
||||
static inline void free_c_VelopackLocatorConfig_vec(vpkc_locator_config_t** arr, size_t count) {
|
||||
if (arr == nullptr || count < 1) { return; }
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
free_c_VelopackLocatorConfig(arr[i]);
|
||||
}
|
||||
delete[] arr;
|
||||
}
|
||||
|
||||
/** An individual Velopack asset, could refer to an asset on-disk or in a remote package feed. */
|
||||
struct VelopackAsset {
|
||||
/** The name or Id of the package containing this release. */
|
||||
std::string PackageId;
|
||||
/** The version of this release. */
|
||||
std::string Version;
|
||||
/** The type of asset (eg. "Full" or "Delta"). */
|
||||
std::string Type;
|
||||
/** The filename of the update package containing this release. */
|
||||
std::string FileName;
|
||||
/** The SHA1 checksum of the update package containing this release. */
|
||||
std::string SHA1;
|
||||
/** The SHA256 checksum of the update package containing this release. */
|
||||
std::string SHA256;
|
||||
/** The size in bytes of the update package containing this release. */
|
||||
uint64_t Size;
|
||||
/** The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string. */
|
||||
std::string NotesMarkdown;
|
||||
/** The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string. */
|
||||
std::string NotesHtml;
|
||||
};
|
||||
|
||||
static inline std::optional<VelopackAsset> to_cpp_VelopackAsset(const vpkc_asset_t* dto) {
|
||||
if (dto == nullptr) { return std::nullopt; }
|
||||
return std::optional<VelopackAsset>({
|
||||
to_cpp_string(dto->PackageId).value(),
|
||||
to_cpp_string(dto->Version).value(),
|
||||
to_cpp_string(dto->Type).value(),
|
||||
to_cpp_string(dto->FileName).value(),
|
||||
to_cpp_string(dto->SHA1).value(),
|
||||
to_cpp_string(dto->SHA256).value(),
|
||||
dto->Size,
|
||||
to_cpp_string(dto->NotesMarkdown).value(),
|
||||
to_cpp_string(dto->NotesHtml).value(),
|
||||
});
|
||||
}
|
||||
|
||||
static inline std::vector<VelopackAsset> to_cpp_VelopackAsset_vec(const vpkc_asset_t* const* arr, size_t c) {
|
||||
if (arr == nullptr || c < 1) { return std::vector<VelopackAsset>(); }
|
||||
std::vector<VelopackAsset> result;
|
||||
result.reserve(c);
|
||||
for (size_t i = 0; i < c; ++i) {
|
||||
auto dto = arr[i];
|
||||
if (dto == nullptr) { continue; }
|
||||
result.push_back(to_cpp_VelopackAsset(dto).value());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline vpkc_asset_t* alloc_c_VelopackAsset(const VelopackAsset* dto) {
|
||||
if (dto == nullptr) { return nullptr; }
|
||||
vpkc_asset_t* obj = new vpkc_asset_t{};
|
||||
obj->PackageId = alloc_c_string(dto->PackageId);
|
||||
obj->Version = alloc_c_string(dto->Version);
|
||||
obj->Type = alloc_c_string(dto->Type);
|
||||
obj->FileName = alloc_c_string(dto->FileName);
|
||||
obj->SHA1 = alloc_c_string(dto->SHA1);
|
||||
obj->SHA256 = alloc_c_string(dto->SHA256);
|
||||
obj->Size = dto->Size;
|
||||
obj->NotesMarkdown = alloc_c_string(dto->NotesMarkdown);
|
||||
obj->NotesHtml = alloc_c_string(dto->NotesHtml);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static inline vpkc_asset_t* alloc_c_VelopackAsset(const std::optional<VelopackAsset>& dto) {
|
||||
if (!dto.has_value()) { return nullptr; }
|
||||
return alloc_c_VelopackAsset(dto.value());
|
||||
}
|
||||
|
||||
static inline vpkc_asset_t** alloc_c_VelopackAsset_vec(const std::vector<VelopackAsset>& dto, size_t* count) {
|
||||
if (dto.empty()) {
|
||||
*count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
*count = dto.size();
|
||||
vpkc_asset_t** arr = new vpkc_asset_t*[*count];
|
||||
for (size_t i = 0; i < *count; ++i) {
|
||||
arr[i] = alloc_c_VelopackAsset(dto[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
static inline void free_c_VelopackAsset(vpkc_asset_t* obj) {
|
||||
if (obj == nullptr) { return; }
|
||||
free_c_string(obj->PackageId);
|
||||
free_c_string(obj->Version);
|
||||
free_c_string(obj->Type);
|
||||
free_c_string(obj->FileName);
|
||||
free_c_string(obj->SHA1);
|
||||
free_c_string(obj->SHA256);
|
||||
|
||||
free_c_string(obj->NotesMarkdown);
|
||||
free_c_string(obj->NotesHtml);
|
||||
delete obj;
|
||||
}
|
||||
|
||||
static inline void free_c_VelopackAsset_vec(vpkc_asset_t** arr, size_t count) {
|
||||
if (arr == nullptr || count < 1) { return; }
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
free_c_VelopackAsset(arr[i]);
|
||||
}
|
||||
delete[] arr;
|
||||
}
|
||||
|
||||
/** Holds information about the current version and pending updates, such as how many there are, and access to release notes. */
|
||||
struct UpdateInfo {
|
||||
/** The available version that we are updating to. */
|
||||
VelopackAsset TargetFullRelease;
|
||||
/** The base release that this update is based on. This is only available if the update is a delta update. */
|
||||
std::optional<VelopackAsset> BaseRelease;
|
||||
/** The list of delta updates that can be applied to the base version to get to the target version. */
|
||||
std::vector<VelopackAsset> DeltasToTarget;
|
||||
/**
|
||||
* True if the update is a version downgrade or lateral move (such as when switching channels to the same version number).
|
||||
* In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be
|
||||
* deleted.
|
||||
*/
|
||||
bool IsDowngrade;
|
||||
};
|
||||
|
||||
static inline std::optional<UpdateInfo> to_cpp_UpdateInfo(const vpkc_update_info_t* dto) {
|
||||
if (dto == nullptr) { return std::nullopt; }
|
||||
return std::optional<UpdateInfo>({
|
||||
to_cpp_VelopackAsset(dto->TargetFullRelease).value(),
|
||||
to_cpp_VelopackAsset(dto->BaseRelease),
|
||||
to_cpp_VelopackAsset_vec(dto->DeltasToTarget, dto->DeltasToTargetCount),
|
||||
dto->IsDowngrade,
|
||||
});
|
||||
}
|
||||
|
||||
static inline std::vector<UpdateInfo> to_cpp_UpdateInfo_vec(const vpkc_update_info_t* const* arr, size_t c) {
|
||||
if (arr == nullptr || c < 1) { return std::vector<UpdateInfo>(); }
|
||||
std::vector<UpdateInfo> result;
|
||||
result.reserve(c);
|
||||
for (size_t i = 0; i < c; ++i) {
|
||||
auto dto = arr[i];
|
||||
if (dto == nullptr) { continue; }
|
||||
result.push_back(to_cpp_UpdateInfo(dto).value());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline vpkc_update_info_t* alloc_c_UpdateInfo(const UpdateInfo* dto) {
|
||||
if (dto == nullptr) { return nullptr; }
|
||||
vpkc_update_info_t* obj = new vpkc_update_info_t{};
|
||||
obj->TargetFullRelease = alloc_c_VelopackAsset(dto->TargetFullRelease);
|
||||
obj->BaseRelease = alloc_c_VelopackAsset(dto->BaseRelease);
|
||||
obj->DeltasToTarget = alloc_c_VelopackAsset_vec(dto->DeltasToTarget, &obj->DeltasToTargetCount);
|
||||
obj->IsDowngrade = dto->IsDowngrade;
|
||||
return obj;
|
||||
}
|
||||
|
||||
static inline vpkc_update_info_t* alloc_c_UpdateInfo(const std::optional<UpdateInfo>& dto) {
|
||||
if (!dto.has_value()) { return nullptr; }
|
||||
return alloc_c_UpdateInfo(dto.value());
|
||||
}
|
||||
|
||||
static inline vpkc_update_info_t** alloc_c_UpdateInfo_vec(const std::vector<UpdateInfo>& dto, size_t* count) {
|
||||
if (dto.empty()) {
|
||||
*count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
*count = dto.size();
|
||||
vpkc_update_info_t** arr = new vpkc_update_info_t*[*count];
|
||||
for (size_t i = 0; i < *count; ++i) {
|
||||
arr[i] = alloc_c_UpdateInfo(dto[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
static inline void free_c_UpdateInfo(vpkc_update_info_t* obj) {
|
||||
if (obj == nullptr) { return; }
|
||||
free_c_VelopackAsset(obj->TargetFullRelease);
|
||||
free_c_VelopackAsset(obj->BaseRelease);
|
||||
free_c_VelopackAsset_vec(obj->DeltasToTarget, obj->DeltasToTargetCount);
|
||||
|
||||
delete obj;
|
||||
}
|
||||
|
||||
static inline void free_c_UpdateInfo_vec(vpkc_update_info_t** arr, size_t count) {
|
||||
if (arr == nullptr || count < 1) { return; }
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
free_c_UpdateInfo(arr[i]);
|
||||
}
|
||||
delete[] arr;
|
||||
}
|
||||
|
||||
/** Options to customise the behaviour of UpdateManager. */
|
||||
struct UpdateOptions {
|
||||
/**
|
||||
* Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading).
|
||||
* This could happen if a release has bugs and was retracted from the release feed, or if you're using
|
||||
* ExplicitChannel to switch channels to another channel where the latest version on that
|
||||
* channel is lower than the current version.
|
||||
*/
|
||||
bool AllowVersionDowngrade;
|
||||
/**
|
||||
* **This option should usually be left None**.
|
||||
* Overrides the default channel used to fetch updates.
|
||||
* The default channel will be whatever channel was specified on the command line when building this release.
|
||||
* For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'.
|
||||
* This allows users to automatically receive updates from the same channel they installed from. This options
|
||||
* allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel
|
||||
* without having to reinstall the application.
|
||||
*/
|
||||
std::optional<std::string> ExplicitChannel;
|
||||
/**
|
||||
* Sets the maximum number of deltas to consider before falling back to a full update.
|
||||
* The default is 10. Set to a negative number (eg. -1) to disable deltas.
|
||||
*/
|
||||
int32_t MaximumDeltasBeforeFallback;
|
||||
};
|
||||
|
||||
static inline std::optional<UpdateOptions> to_cpp_UpdateOptions(const vpkc_update_options_t* dto) {
|
||||
if (dto == nullptr) { return std::nullopt; }
|
||||
return std::optional<UpdateOptions>({
|
||||
dto->AllowVersionDowngrade,
|
||||
to_cpp_string(dto->ExplicitChannel),
|
||||
dto->MaximumDeltasBeforeFallback,
|
||||
});
|
||||
}
|
||||
|
||||
static inline std::vector<UpdateOptions> to_cpp_UpdateOptions_vec(const vpkc_update_options_t* const* arr, size_t c) {
|
||||
if (arr == nullptr || c < 1) { return std::vector<UpdateOptions>(); }
|
||||
std::vector<UpdateOptions> result;
|
||||
result.reserve(c);
|
||||
for (size_t i = 0; i < c; ++i) {
|
||||
auto dto = arr[i];
|
||||
if (dto == nullptr) { continue; }
|
||||
result.push_back(to_cpp_UpdateOptions(dto).value());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline vpkc_update_options_t* alloc_c_UpdateOptions(const UpdateOptions* dto) {
|
||||
if (dto == nullptr) { return nullptr; }
|
||||
vpkc_update_options_t* obj = new vpkc_update_options_t{};
|
||||
obj->AllowVersionDowngrade = dto->AllowVersionDowngrade;
|
||||
obj->ExplicitChannel = alloc_c_string(dto->ExplicitChannel);
|
||||
obj->MaximumDeltasBeforeFallback = dto->MaximumDeltasBeforeFallback;
|
||||
return obj;
|
||||
}
|
||||
|
||||
static inline vpkc_update_options_t* alloc_c_UpdateOptions(const std::optional<UpdateOptions>& dto) {
|
||||
if (!dto.has_value()) { return nullptr; }
|
||||
return alloc_c_UpdateOptions(dto.value());
|
||||
}
|
||||
|
||||
static inline vpkc_update_options_t** alloc_c_UpdateOptions_vec(const std::vector<UpdateOptions>& dto, size_t* count) {
|
||||
if (dto.empty()) {
|
||||
*count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
*count = dto.size();
|
||||
vpkc_update_options_t** arr = new vpkc_update_options_t*[*count];
|
||||
for (size_t i = 0; i < *count; ++i) {
|
||||
arr[i] = alloc_c_UpdateOptions(dto[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
static inline void free_c_UpdateOptions(vpkc_update_options_t* obj) {
|
||||
if (obj == nullptr) { return; }
|
||||
|
||||
free_c_string(obj->ExplicitChannel);
|
||||
|
||||
delete obj;
|
||||
}
|
||||
|
||||
static inline void free_c_UpdateOptions_vec(vpkc_update_options_t** arr, size_t count) {
|
||||
if (arr == nullptr || count < 1) { return; }
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
free_c_UpdateOptions(arr[i]);
|
||||
}
|
||||
delete[] arr;
|
||||
}
|
||||
// !! AUTO-GENERATED-END CPP_TYPES
|
||||
|
||||
/**
|
||||
* VelopackApp helps you to handle app activation events correctly.
|
||||
* This should be used as early as possible in your application startup code.
|
||||
@@ -273,9 +483,10 @@ public:
|
||||
* Override the command line arguments used by VelopackApp. (by default this is env::args().skip(1))
|
||||
*/
|
||||
VelopackApp& SetArgs(const std::vector<std::string>& args) {
|
||||
char** pArgs = allocate_cstring_array(args);
|
||||
vpkc_app_set_args(pArgs, args.size());
|
||||
free_cstring_array(pArgs, args.size());
|
||||
size_t c;
|
||||
char** pArgs = alloc_c_string_vec(args, &c);
|
||||
vpkc_app_set_args(pArgs, c);
|
||||
free_c_string_vec(pArgs, c);
|
||||
return *this;
|
||||
};
|
||||
|
||||
@@ -283,8 +494,9 @@ public:
|
||||
* VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth).
|
||||
*/
|
||||
VelopackApp& SetLocator(const VelopackLocatorConfig& locator) {
|
||||
vpkc_locator_config_t vpkc_locator = to_c(locator);
|
||||
vpkc_app_set_locator(&vpkc_locator);
|
||||
vpkc_locator_config_t* vpkc_locator = alloc_c_VelopackLocatorConfig(locator);
|
||||
vpkc_app_set_locator(vpkc_locator);
|
||||
free_c_VelopackLocatorConfig(vpkc_locator);
|
||||
return *this;
|
||||
};
|
||||
|
||||
@@ -360,7 +572,7 @@ public:
|
||||
/**
|
||||
* Progress callback function. Call with values between 0 and 100 inclusive.
|
||||
*/
|
||||
typedef std::function<void(size_t)> vpkc_progress_send_t;
|
||||
typedef std::function<void(int16_t)> vpkc_progress_send_t;
|
||||
|
||||
/**
|
||||
* Abstract class for retrieving release feeds and downloading assets. You should subclass this and
|
||||
@@ -385,16 +597,16 @@ public:
|
||||
[](void* userData, const char* releasesName) {
|
||||
IUpdateSource* source = reinterpret_cast<IUpdateSource*>(userData);
|
||||
std::string json = source->GetReleaseFeed(releasesName);
|
||||
return allocate_cstring(json);
|
||||
return alloc_c_string(json);
|
||||
},
|
||||
[](void* userData, char* pszFeed) {
|
||||
free_cstring(pszFeed);
|
||||
free_c_string(pszFeed);
|
||||
},
|
||||
[](void* userData, const struct vpkc_asset_t *pAsset, const char* pszLocalPath, size_t progressCallbackId) {
|
||||
IUpdateSource* source = reinterpret_cast<IUpdateSource*>(userData);
|
||||
VelopackAsset asset = to_cpp(*pAsset);
|
||||
std::string localPath = to_cppstring(pszLocalPath);
|
||||
std::function<void(size_t)> progress_callback = [progressCallbackId](size_t progress) {
|
||||
VelopackAsset asset = to_cpp_VelopackAsset(pAsset).value();
|
||||
std::string localPath = to_cpp_string(pszLocalPath).value();
|
||||
std::function<void(int16_t)> progress_callback = [progressCallbackId](int16_t progress) {
|
||||
vpkc_source_report_progress(progressCallbackId, progress);
|
||||
};
|
||||
return source->DownloadReleaseEntry(asset, localPath, progress_callback);
|
||||
@@ -457,21 +669,12 @@ public:
|
||||
* @param locator Override the default locator configuration (usually used for testing / mocks).
|
||||
*/
|
||||
UpdateManager(const std::string& urlOrPath, const UpdateOptions* options = nullptr, const VelopackLocatorConfig* locator = nullptr) {
|
||||
vpkc_update_options_t vpkc_options;
|
||||
vpkc_update_options_t* pOptions = nullptr;
|
||||
if (options != nullptr) {
|
||||
vpkc_options = to_c(*options);
|
||||
pOptions = &vpkc_options;
|
||||
}
|
||||
|
||||
vpkc_locator_config_t vpkc_locator;
|
||||
vpkc_locator_config_t* pLocator = nullptr;
|
||||
if (locator != nullptr) {
|
||||
vpkc_locator = to_c(*locator);
|
||||
pLocator = &vpkc_locator;
|
||||
}
|
||||
|
||||
if (!vpkc_new_update_manager(urlOrPath.c_str(), pOptions, pLocator, &m_pManager)) {
|
||||
vpkc_update_options_t* pOptions = alloc_c_UpdateOptions(options);
|
||||
vpkc_locator_config_t* pLocator = alloc_c_VelopackLocatorConfig(locator);
|
||||
bool result = vpkc_new_update_manager(urlOrPath.c_str(), pOptions, pLocator, &m_pManager);
|
||||
free_c_UpdateOptions(pOptions);
|
||||
free_c_VelopackLocatorConfig(pLocator);
|
||||
if (!result) {
|
||||
throw_last_error();
|
||||
}
|
||||
};
|
||||
@@ -484,23 +687,14 @@ public:
|
||||
*/
|
||||
template <typename T, typename = std::enable_if_t<std::is_base_of_v<IUpdateSource, T>>>
|
||||
UpdateManager(std::unique_ptr<T> pUpdateSource, const UpdateOptions* options = nullptr, const VelopackLocatorConfig* locator = nullptr) {
|
||||
vpkc_update_options_t vpkc_options;
|
||||
vpkc_update_options_t* pOptions = nullptr;
|
||||
if (options != nullptr) {
|
||||
vpkc_options = to_c(*options);
|
||||
pOptions = &vpkc_options;
|
||||
}
|
||||
|
||||
vpkc_locator_config_t vpkc_locator;
|
||||
vpkc_locator_config_t* pLocator = nullptr;
|
||||
if (locator != nullptr) {
|
||||
vpkc_locator = to_c(*locator);
|
||||
pLocator = &vpkc_locator;
|
||||
}
|
||||
|
||||
vpkc_update_options_t* pOptions = alloc_c_UpdateOptions(options);
|
||||
vpkc_locator_config_t* pLocator = alloc_c_VelopackLocatorConfig(locator);
|
||||
m_pUpdateSource = std::unique_ptr<IUpdateSource>(static_cast<IUpdateSource*>(pUpdateSource.release()));
|
||||
vpkc_update_source_t* pSource = m_pUpdateSource->m_pSource;
|
||||
if (!vpkc_new_update_manager_with_source(pSource, pOptions, pLocator, &m_pManager)) {
|
||||
bool result = vpkc_new_update_manager_with_source(pSource, pOptions, pLocator, &m_pManager);
|
||||
free_c_UpdateOptions(pOptions);
|
||||
free_c_VelopackLocatorConfig(pLocator);
|
||||
if (!result) {
|
||||
throw_last_error();
|
||||
}
|
||||
};
|
||||
@@ -545,10 +739,10 @@ public:
|
||||
* You can pass the UpdateInfo object to waitExitThenApplyUpdate to apply the update.
|
||||
*/
|
||||
std::optional<VelopackAsset> UpdatePendingRestart() noexcept {
|
||||
vpkc_asset_t asset;
|
||||
vpkc_asset_t* asset;
|
||||
if (vpkc_update_pending_restart(m_pManager, &asset)) {
|
||||
VelopackAsset cpp_asset = to_cpp(asset);
|
||||
vpkc_free_asset(&asset);
|
||||
VelopackAsset cpp_asset = to_cpp_VelopackAsset(asset).value();
|
||||
vpkc_free_asset(asset);
|
||||
return cpp_asset;
|
||||
}
|
||||
return std::nullopt;
|
||||
@@ -559,7 +753,7 @@ public:
|
||||
* UpdateInfo object containing the latest available release, and any delta updates that can be applied if they are available.
|
||||
*/
|
||||
std::optional<UpdateInfo> CheckForUpdates() {
|
||||
vpkc_update_info_t update;
|
||||
vpkc_update_info_t* update;
|
||||
vpkc_update_check_t result = vpkc_check_for_updates(m_pManager, &update);
|
||||
switch (result) {
|
||||
case vpkc_update_check_t::UPDATE_ERROR:
|
||||
@@ -569,8 +763,8 @@ public:
|
||||
case vpkc_update_check_t::REMOTE_IS_EMPTY:
|
||||
return std::nullopt;
|
||||
case vpkc_update_check_t::UPDATE_AVAILABLE:
|
||||
UpdateInfo cpp_info = to_cpp(update);
|
||||
vpkc_free_update_info(&update);
|
||||
UpdateInfo cpp_info = to_cpp_UpdateInfo(update).value();
|
||||
vpkc_free_update_info(update);
|
||||
return cpp_info;
|
||||
}
|
||||
return std::nullopt;
|
||||
@@ -585,8 +779,10 @@ public:
|
||||
* packages, this method will fall back to downloading the full version of the update.
|
||||
*/
|
||||
void DownloadUpdates(const UpdateInfo& update, vpkc_progress_callback_t progress = nullptr, void* pUserData = 0) {
|
||||
vpkc_update_info_t vpkc_update = to_c(update);
|
||||
if (!vpkc_download_updates(m_pManager, &vpkc_update, progress, pUserData)) {
|
||||
vpkc_update_info_t* vpkc_update = alloc_c_UpdateInfo(update);
|
||||
bool result = vpkc_download_updates(m_pManager, vpkc_update, progress, pUserData);
|
||||
free_c_UpdateInfo(vpkc_update);
|
||||
if (!result) {
|
||||
throw_last_error();
|
||||
}
|
||||
};
|
||||
@@ -606,11 +802,12 @@ public:
|
||||
* optionally restart your app. The updater will only wait for 60 seconds before giving up.
|
||||
*/
|
||||
void WaitExitThenApplyUpdates(const VelopackAsset& asset, bool silent = false, bool restart = true, std::vector<std::string> restartArgs = {}) {
|
||||
char** pRestartArgs = allocate_cstring_array(restartArgs);
|
||||
vpkc_asset_t vpkc_asset = to_c(asset);
|
||||
bool result = vpkc_wait_exit_then_apply_updates(m_pManager, &vpkc_asset, silent, restart, pRestartArgs, restartArgs.size());
|
||||
free_cstring_array(pRestartArgs, restartArgs.size());
|
||||
|
||||
size_t cRestartArgs;
|
||||
char** pRestartArgs = alloc_c_string_vec(restartArgs, &cRestartArgs);
|
||||
vpkc_asset_t* vpkc_asset = alloc_c_VelopackAsset(asset);
|
||||
bool result = vpkc_wait_exit_then_apply_updates(m_pManager, vpkc_asset, silent, restart, pRestartArgs, cRestartArgs);
|
||||
free_c_string_vec(pRestartArgs, cRestartArgs);
|
||||
free_c_VelopackAsset(vpkc_asset);
|
||||
if (!result) {
|
||||
throw_last_error();
|
||||
}
|
||||
@@ -623,11 +820,12 @@ public:
|
||||
* If waitPid is 0, the updater will not wait for any process to exit before applying updates (Not Recommended).
|
||||
*/
|
||||
void UnsafeApplyUpdates(const VelopackAsset& asset, bool silent, uint32_t waitPid, bool restart, std::vector<std::string> restartArgs) {
|
||||
char** pRestartArgs = allocate_cstring_array(restartArgs);
|
||||
vpkc_asset_t vpkc_asset = to_c(asset);
|
||||
bool result = vpkc_unsafe_apply_updates(m_pManager, &vpkc_asset, silent, waitPid, restart, pRestartArgs, restartArgs.size());
|
||||
free_cstring_array(pRestartArgs, restartArgs.size());
|
||||
|
||||
size_t cRestartArgs;
|
||||
char** pRestartArgs = alloc_c_string_vec(restartArgs, &cRestartArgs);
|
||||
vpkc_asset_t* vpkc_asset = alloc_c_VelopackAsset(asset);
|
||||
bool result = vpkc_unsafe_apply_updates(m_pManager, vpkc_asset, silent, waitPid, restart, pRestartArgs, cRestartArgs);
|
||||
free_c_string_vec(pRestartArgs, cRestartArgs);
|
||||
free_c_VelopackAsset(vpkc_asset);
|
||||
if (!result) {
|
||||
throw_last_error();
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ pub struct vpkc_asset_t {
|
||||
pub PackageId: *mut c_char,
|
||||
/// The version of this release.
|
||||
pub Version: *mut c_char,
|
||||
/// The type of asset (eg. "Full" or "Delta").
|
||||
/// The type of asset (eg. "Full" or "Delta").
|
||||
pub Type: *mut c_char,
|
||||
/// The filename of the update package containing this release.
|
||||
pub FileName: *mut c_char,
|
||||
@@ -347,8 +347,8 @@ pub struct vpkc_update_info_t {
|
||||
/// The number of elements in the DeltasToTarget array.
|
||||
pub DeltasToTargetCount: size_t,
|
||||
/// True if the update is a version downgrade or lateral move (such as when switching channels to the same version number).
|
||||
/// In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be
|
||||
/// deleted.
|
||||
/// In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be
|
||||
/// deleted.
|
||||
pub IsDowngrade: bool,
|
||||
}
|
||||
|
||||
@@ -437,20 +437,20 @@ pub unsafe fn free_UpdateInfo_vec(obj: *mut *mut vpkc_update_info_t, count: size
|
||||
/// Options to customise the behaviour of UpdateManager.
|
||||
pub struct vpkc_update_options_t {
|
||||
/// Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading).
|
||||
/// This could happen if a release has bugs and was retracted from the release feed, or if you're using
|
||||
/// ExplicitChannel to switch channels to another channel where the latest version on that
|
||||
/// channel is lower than the current version.
|
||||
/// This could happen if a release has bugs and was retracted from the release feed, or if you're using
|
||||
/// ExplicitChannel to switch channels to another channel where the latest version on that
|
||||
/// channel is lower than the current version.
|
||||
pub AllowVersionDowngrade: bool,
|
||||
/// **This option should usually be left None**.
|
||||
/// Overrides the default channel used to fetch updates.
|
||||
/// The default channel will be whatever channel was specified on the command line when building this release.
|
||||
/// For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'.
|
||||
/// This allows users to automatically receive updates from the same channel they installed from. This options
|
||||
/// allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel
|
||||
/// without having to reinstall the application.
|
||||
/// Overrides the default channel used to fetch updates.
|
||||
/// The default channel will be whatever channel was specified on the command line when building this release.
|
||||
/// For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'.
|
||||
/// This allows users to automatically receive updates from the same channel they installed from. This options
|
||||
/// allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel
|
||||
/// without having to reinstall the application.
|
||||
pub ExplicitChannel: *mut c_char,
|
||||
/// Sets the maximum number of deltas to consider before falling back to a full update.
|
||||
/// The default is 10. Set to a negative number (eg. -1) to disable deltas.
|
||||
/// The default is 10. Set to a negative number (eg. -1) to disable deltas.
|
||||
pub MaximumDeltasBeforeFallback: i32,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
||||
public void AppendDocComment(string comment)
|
||||
{
|
||||
if (comment != null) {
|
||||
foreach (var line in comment.ReplaceLineEndings("\n").Split('\n')) {
|
||||
AppendLine($"/// {line}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,6 @@ string[] desiredStructs = [
|
||||
"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();
|
||||
|
||||
@@ -48,79 +41,44 @@ if (desiredStructs.Length != availableStructs.Count) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// rust bridge code
|
||||
// string rustCppLib = Path.Combine(libcppDir, "src", "lib.rs");
|
||||
string rustTypes = Path.Combine(libcppDir, "src", "types.rs");
|
||||
//string rustCppMap = Path.Combine(libcppDir, "src", "map.rs");
|
||||
string rustCppInclude = Path.Combine(libcppDir, "include", "Velopack.hpp");
|
||||
//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();
|
||||
// cTypes.AppendLine();
|
||||
// foreach (var rs in availableStructs) {
|
||||
// Templates.WriteBasicC(basic_libc_names, cTypes, rs);
|
||||
// }
|
||||
|
||||
Console.WriteLine("Generating C++ types");
|
||||
var cppTypes = new IndentStringBuilder();
|
||||
cppTypes.AppendLine();
|
||||
foreach (var rs in availableStructs) {
|
||||
Templates.WriteCPlusPlus(basic_libc_names, cppTypes, rs);
|
||||
}
|
||||
foreach (var rs in availableStructs) {
|
||||
Templates.WriteC2CPPMapping(basic_libc_names, cppTypes, rs);
|
||||
}
|
||||
|
||||
Console.WriteLine("Generating Rust-C types");
|
||||
//var rustCTypes = new IndentStringBuilder();
|
||||
//foreach (var rs in availableStructs) {
|
||||
// Templates.WriteRustCRepr(basic_libc_names, rustCTypes, rs);
|
||||
//}
|
||||
var rustCTypesTemplate = Handlebars.Compile(File.ReadAllText(Path.Combine(templatesDir, "rust_struct.hbs")));
|
||||
Handlebars.RegisterHelper("indent", (writer, context, args) => {
|
||||
var comment = (string) context[(string) args[0]];
|
||||
var indent = (string) args[1];
|
||||
writer.WriteSafeString(comment.PrefixEveryLine(indent));
|
||||
writer.WriteSafeString("\n");
|
||||
});
|
||||
|
||||
var types = new List<TypeMap>() {
|
||||
new TypeMap("VelopackAsset", "vpkc_asset_t", "vpkc_asset_t", false),
|
||||
new TypeMap("UpdateInfo", "vpkc_update_info_t", "vpkc_update_info_t", false),
|
||||
new TypeMap("UpdateOptions", "vpkc_update_options_t", "vpkc_update_options_t", false),
|
||||
new TypeMap("VelopackLocatorConfig", "vpkc_locator_config_t", "vpkc_locator_config_t", false),
|
||||
new TypeMap("String", "char", "c_char", false),
|
||||
new TypeMap("PathBuf", "char", "c_char", false),
|
||||
new TypeMap("bool", "bool", "bool", true),
|
||||
new TypeMap("i32", "int32_t", "i32", true),
|
||||
new TypeMap("i64", "int64_t", "i64", true),
|
||||
new TypeMap("u32", "uint32_t", "u32", true),
|
||||
new TypeMap("u64", "uint64_t", "u64", true),
|
||||
TypeMap.RustStruct("VelopackAsset", "vpkc_asset_t"),
|
||||
TypeMap.RustStruct("UpdateInfo", "vpkc_update_info_t"),
|
||||
TypeMap.RustStruct("UpdateOptions", "vpkc_update_options_t"),
|
||||
TypeMap.RustStruct("VelopackLocatorConfig", "vpkc_locator_config_t"),
|
||||
TypeMap.SystemType("String", "char", "string", "c_char"),
|
||||
TypeMap.SystemType("PathBuf", "char", "string", "c_char"),
|
||||
TypeMap.Primitive("bool", "bool"),
|
||||
TypeMap.Primitive("i32", "int32_t"),
|
||||
TypeMap.Primitive("i64", "int64_t"),
|
||||
TypeMap.Primitive("u32", "uint32_t"),
|
||||
TypeMap.Primitive("u64", "uint64_t"),
|
||||
}.ToDictionary(v => v.rustType, v => v);
|
||||
|
||||
var data = availableStructs.Select(s => new RustStruct_Struct {
|
||||
rust_comment = s.DocComment.PrefixEveryLine("/// "),
|
||||
var handlebarData = availableStructs.Select(s => new RustStruct_Struct {
|
||||
rust_comment = s.DocComment.ToRustComment(),
|
||||
cpp_comment = s.DocComment.ToCppComment(),
|
||||
struct_rust_name = s.Name,
|
||||
struct_c_name = types[s.Name].interopType,
|
||||
fields = s.Fields.Select(f => {
|
||||
var isString = types[f.Type].rustType == "PathBuf" || types[f.Type].rustType == "String";
|
||||
var field = new RustStruct_Field {
|
||||
rust_comment = f.DocComment.PrefixEveryLine("/// "),
|
||||
rust_comment = f.DocComment.ToRustComment(),
|
||||
cpp_comment = f.DocComment.ToCppComment(),
|
||||
field_name = f.Name,
|
||||
field_optional = f.Optional,
|
||||
field_vector = f.Vec,
|
||||
//field_add_pointer = f.Optional && !f.Vec && !isString,
|
||||
//field_requires_ref = !isString && !f.Vec && !f.Optional,
|
||||
//field_string = isString,
|
||||
field_rust_type = f.Type,
|
||||
field_c_type = types[f.Type].interopType,
|
||||
field_cpp_type = types[f.Type].cppType,
|
||||
field_system = types[f.Type].system,
|
||||
field_primitive = types[f.Type].primitive,
|
||||
field_normal = !f.Vec && !types[f.Type].primitive,
|
||||
};
|
||||
@@ -128,32 +86,72 @@ var data = availableStructs.Select(s => new RustStruct_Struct {
|
||||
}).ToArray(),
|
||||
}).ToArray();
|
||||
|
||||
var rustCTypes = rustCTypesTemplate(data);
|
||||
string rustTypes = Path.Combine(libcppDir, "src", "types.rs");
|
||||
var rustCTypesTemplate = Handlebars.Compile(File.ReadAllText(Path.Combine(templatesDir, "rust_types.hbs")));
|
||||
var rustCTypes = rustCTypesTemplate(handlebarData);
|
||||
|
||||
Console.WriteLine();
|
||||
//Console.WriteLine("Generating C to bridge mappings");
|
||||
//var cToBridgeMapping = new IndentStringBuilder();
|
||||
//foreach (var rs in availableStructs) {
|
||||
// Templates.WriteCBridgeMapping(basic_libc_names, cToBridgeMapping, rs);
|
||||
//}
|
||||
string rustCppInclude = Path.Combine(libcppDir, "include", "Velopack.hpp");
|
||||
var cppTypesTemplate = Handlebars.Compile(File.ReadAllText(Path.Combine(templatesDir, "cpp_mapping.hbs")));
|
||||
var cppTypes = cppTypesTemplate(handlebarData);
|
||||
|
||||
Console.WriteLine("Writing all to file");
|
||||
//Util.ReplaceTextInFile(rustCppLib, "BRIDGE_DTOS", sbBridgeDto.ToString());
|
||||
//Util.ReplaceTextInFile(rustCppMap, "CORE_MAPPING", sbBridgeMapping.ToString());
|
||||
Util.ReplaceTextInFile(rustTypes, "RUST_TYPES", rustCTypes.ToString());
|
||||
// Util.ReplaceTextInFile(rustCppInclude, "C_TYPES", cTypes.ToString());
|
||||
Util.ReplaceTextInFile(rustCppInclude, "CPP_TYPES", cppTypes.ToString());
|
||||
//Util.ReplaceTextInFile(rustBridgeC, "BRIDGE_MAPPING", cToBridgeMapping.ToString());
|
||||
Util.ReplaceTextInFile(rustTypes, "RUST_TYPES", rustCTypes.ToString().ReplaceLineEndings("\n"));
|
||||
Util.ReplaceTextInFile(rustCppInclude, "CPP_TYPES", cppTypes.ToString().ReplaceLineEndings("\n"));
|
||||
|
||||
return 0;
|
||||
|
||||
record struct TypeMap(string rustType, string cType, string interopType, bool primitive);
|
||||
class TypeMap
|
||||
{
|
||||
public string rustType;
|
||||
public string cType;
|
||||
public string cppType;
|
||||
public string interopType;
|
||||
public bool primitive;
|
||||
public bool system;
|
||||
|
||||
public static TypeMap RustStruct(string rustName, string cType)
|
||||
{
|
||||
return new TypeMap() {
|
||||
rustType = rustName,
|
||||
cType = cType,
|
||||
cppType = rustName,
|
||||
interopType = cType,
|
||||
primitive = false,
|
||||
system = false,
|
||||
};
|
||||
}
|
||||
|
||||
public static TypeMap Primitive(string rustName, string cType)
|
||||
{
|
||||
return new TypeMap() {
|
||||
rustType = rustName,
|
||||
cType = cType,
|
||||
cppType = cType,
|
||||
interopType = rustName,
|
||||
primitive = true,
|
||||
system = false,
|
||||
};
|
||||
}
|
||||
|
||||
public static TypeMap SystemType(string rustName, string cType, string cppType, string interopType)
|
||||
{
|
||||
return new TypeMap() {
|
||||
rustType = rustName,
|
||||
cType = cType,
|
||||
cppType = cppType,
|
||||
interopType = interopType,
|
||||
primitive = false,
|
||||
system = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class RustStruct_Struct
|
||||
{
|
||||
public string struct_c_name;
|
||||
public string struct_rust_name;
|
||||
public string rust_comment;
|
||||
public string cpp_comment;
|
||||
public RustStruct_Field[] fields;
|
||||
}
|
||||
|
||||
@@ -161,14 +159,13 @@ class RustStruct_Field
|
||||
{
|
||||
public string field_name;
|
||||
public string field_c_type;
|
||||
public string field_cpp_type;
|
||||
public string field_rust_type;
|
||||
public bool field_primitive;
|
||||
public bool field_optional;
|
||||
public bool field_vector;
|
||||
//public bool field_prefix;
|
||||
//public bool field_add_pointer;
|
||||
//public bool field_requires_ref;
|
||||
public bool field_system;
|
||||
public bool field_normal;
|
||||
//public bool field_string;
|
||||
public string rust_comment;
|
||||
public string cpp_comment;
|
||||
}
|
||||
@@ -1,449 +0,0 @@
|
||||
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 GetBasicCTypeInRust(Dictionary<string, string> nameMap, string rustType)
|
||||
{
|
||||
switch (rustType) {
|
||||
case "PathBuf":
|
||||
case "String":
|
||||
return "*mut c_char";
|
||||
case "bool":
|
||||
return "bool";
|
||||
case "i32":
|
||||
return "i32";
|
||||
case "i64":
|
||||
return "i64";
|
||||
case "u32":
|
||||
return "u32";
|
||||
case "u64":
|
||||
return "u64";
|
||||
default:
|
||||
if (nameMap.TryGetValue(rustType, out var type)) {
|
||||
return type;
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Unsupported type for rust-c: " + rustType);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteRustCRepr(Dictionary<string, string> nameMap, IndentStringBuilder sb, RustStruct rs)
|
||||
{
|
||||
var cName = nameMap[rs.Name];
|
||||
sb.AppendLine("#[rustfmt::skip]");
|
||||
sb.AppendLine($"#[repr(C)]");
|
||||
sb.AppendDocComment(rs.DocComment);
|
||||
sb.AppendLine($"pub struct {cName} {{");
|
||||
using (sb.Indent()) {
|
||||
foreach (var field in rs.Fields) {
|
||||
sb.AppendDocComment(field.DocComment);
|
||||
var basicc = GetBasicCTypeInRust(nameMap, field.Type);
|
||||
if (field.Vec) {
|
||||
sb.AppendLine($"pub {field.Name}: *mut *mut {basicc},");
|
||||
sb.AppendDocComment($"Count for {field.Name} Array");
|
||||
sb.AppendLine($"pub {field.Name}Count: size_t,");
|
||||
} else {
|
||||
string opt = field.Optional && !basicc.StartsWith("*") ? "*mut " : "";
|
||||
sb.AppendLine($"pub {field.Name}: {opt}{basicc},");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
|
||||
sb.AppendLine("#[rustfmt::skip]");
|
||||
sb.AppendLine($"pub fn c_to_{rs.Name.ToLower()}(obj: &{cName}) -> {rs.Name} {{");
|
||||
using (sb.Indent()) {
|
||||
// sb.AppendLine($"let obj = unsafe {{ &*obj }};");
|
||||
sb.AppendLine($"{rs.Name} {{");
|
||||
using (sb.Indent()) {
|
||||
foreach (var field in rs.Fields) {
|
||||
if (field.Vec) {
|
||||
sb.AppendLine($"{field.Name}: c_to_{field.Type.ToLower()}_vec(obj.{field.Name}, obj.{field.Name}Count),");
|
||||
} else if (field.Optional || field.Type == "PathBuf" || field.Type == "String" || nameMap.ContainsKey(field.Type)) {
|
||||
sb.AppendLine($"{field.Name}: c_to_{field.Type.ToLower()}{(field.Optional ? "_opt" : "")}({(nameMap.ContainsKey(field.Type) && !field.Optional ? "&" : "")}obj.{field.Name}),");
|
||||
} else {
|
||||
sb.AppendLine($"{field.Name}: obj.{field.Name},");
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.AppendLine("}");
|
||||
}
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
|
||||
sb.AppendLine("#[rustfmt::skip]");
|
||||
sb.AppendLine($"pub fn c_to_{rs.Name.ToLower()}_opt(obj: *mut {cName}) -> Option<{rs.Name}> {{");
|
||||
using (sb.Indent()) {
|
||||
sb.AppendLine("if obj.is_null() { return None; }");
|
||||
sb.AppendLine($"Some(c_to_{rs.Name.ToLower()}(unsafe {{ &*obj }}))");
|
||||
}
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
|
||||
sb.AppendLine("#[rustfmt::skip]");
|
||||
sb.AppendLine($"pub fn c_to_{rs.Name.ToLower()}_vec(obj: *mut *mut {cName}, count: size_t) -> Vec<{rs.Name}> {{");
|
||||
using (sb.Indent()) {
|
||||
sb.AppendLine("if obj.is_null() || count == 0 { return Vec::new(); }");
|
||||
sb.AppendLine("let mut assets = Vec::with_capacity(count as usize);");
|
||||
sb.AppendLine("for i in 0..count {");
|
||||
using (sb.Indent()) {
|
||||
sb.AppendLine("let ptr = unsafe { *obj.add(i as usize) };");
|
||||
sb.AppendLine($"assets.push(c_to_{rs.Name.ToLower()}(unsafe {{ &*ptr }}));");
|
||||
}
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine("assets");
|
||||
}
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
|
||||
sb.AppendLine("#[rustfmt::skip]");
|
||||
sb.AppendLine($"pub unsafe fn allocate_{rs.Name.ToLower()}(dto: {rs.Name}, obj: *mut {cName}) {{");
|
||||
using (sb.Indent()) {
|
||||
sb.AppendLine("if obj.is_null() { return; }");
|
||||
sb.AppendLine($"log::debug!(\"{cName} allocated\");");
|
||||
foreach (var field in rs.Fields) {
|
||||
if (field.Optional || field.Type == "PathBuf" || field.Type == "String" || nameMap.ContainsKey(field.Type)) {
|
||||
if (field.Optional && !(field.Type == "PathBuf" || field.Type == "String")) {
|
||||
sb.AppendLine($"if let Some(opt) = dto.{field.Name} {{");
|
||||
using (sb.Indent()) {
|
||||
var fieldcName = nameMap[field.Type];
|
||||
sb.AppendLine($"let ptr = libc::malloc(size_of::<{fieldcName}>()) as *mut {fieldcName};");
|
||||
sb.AppendLine($"(*obj).{field.Name} = ptr;");
|
||||
sb.AppendLine($"allocate_{field.Type.ToLower()}(opt, ptr);");
|
||||
}
|
||||
sb.AppendLine($"}}");
|
||||
} else {
|
||||
sb.AppendLine($"allocate_{field.Type.ToLower()}(dto.{field.Name}, &mut (*obj).{field.Name});");
|
||||
}
|
||||
} else {
|
||||
sb.AppendLine($"(*obj).{field.Name} = dto.{field.Name};");
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
|
||||
sb.AppendLine("#[rustfmt::skip]");
|
||||
sb.AppendLine($"pub unsafe fn free_{rs.Name.ToLower()}(obj: *mut {cName}) {{");
|
||||
using (sb.Indent()) {
|
||||
sb.AppendLine("if obj.is_null() { return; }");
|
||||
sb.AppendLine($"log::debug!(\"{cName} freed\");");
|
||||
foreach (var field in rs.Fields) {
|
||||
if (field.Optional || field.Type == "PathBuf" || field.Type == "String" || nameMap.ContainsKey(field.Type)) {
|
||||
if (field.Optional) {
|
||||
sb.AppendLine($"free_{field.Type.ToLower()}((*obj).{field.Name});");
|
||||
} else {
|
||||
sb.AppendLine($"free_{field.Type.ToLower()}(&mut (*obj).{field.Name});");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
private static string GetCPlusPlusType(string[] coreTypes, string rustType, bool optional, bool vec)
|
||||
{
|
||||
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),
|
||||
};
|
||||
|
||||
type = vec ? "std::vector<" + type + ">" : type;
|
||||
type = optional ? "std::optional<" + type + ">" : type;
|
||||
return 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.AppendDocComment(rs.DocComment);
|
||||
sb.AppendLine($"typedef struct {nameMap[rs.Name]} {{");
|
||||
foreach (var field in rs.Fields) {
|
||||
using (sb.Indent()) {
|
||||
sb.AppendDocComment(field.DocComment);
|
||||
sb.AppendLine($"{GetBasicCType(nameMap, field.Type)} {field.Name};");
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine($"}} {nameMap[rs.Name]};");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
public static void WriteC2CPPMapping(Dictionary<string, string> nameMap, IndentStringBuilder sb, RustStruct rs)
|
||||
{
|
||||
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 WriteCPlusPlus(Dictionary<string, string> nameMap, IndentStringBuilder sb, RustStruct rs)
|
||||
{
|
||||
var coreTypes = nameMap.Keys.ToArray();
|
||||
sb.AppendDocComment(rs.DocComment);
|
||||
sb.AppendLine($"struct {rs.Name} {{");
|
||||
foreach (var field in rs.Fields) {
|
||||
using (sb.Indent()) {
|
||||
sb.AppendDocComment(field.DocComment);
|
||||
sb.AppendLine($"{GetCPlusPlusType(coreTypes, field.Type, field.Optional, field.Vec)} {field.Name};");
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
82
src/lib-cpp/type-generator/Templates/cpp_mapping.hbs
Normal file
82
src/lib-cpp/type-generator/Templates/cpp_mapping.hbs
Normal file
@@ -0,0 +1,82 @@
|
||||
{{#each this}}
|
||||
|
||||
{{cpp_comment}}
|
||||
struct {{struct_rust_name}} {
|
||||
{{#each fields}}
|
||||
{{#indent "cpp_comment" " "}}
|
||||
{{#unless field_vector}}{{#if field_optional}}std::optional<{{/if~}}
|
||||
{{~#if field_system~}}std::{{~/if~}}{{field_cpp_type}}
|
||||
{{~#if field_optional}}>{{/if}} {{field_name}};{{~/unless~}}
|
||||
{{#if field_vector}}std::vector<{{field_cpp_type}}> {{field_name}};{{/if}}
|
||||
{{/each}}
|
||||
};
|
||||
|
||||
static inline std::optional<{{struct_rust_name}}> to_cpp_{{struct_rust_name}}(const {{struct_c_name}}* dto) {
|
||||
if (dto == nullptr) { return std::nullopt; }
|
||||
return std::optional<{{struct_rust_name}}>({
|
||||
{{#each fields}}
|
||||
{{#if field_primitive}}dto->{{field_name}},{{/if~}}
|
||||
{{#if field_normal}}to_cpp_{{field_cpp_type}}(dto->{{field_name}}){{~#unless field_optional}}.value(){{/unless}},{{/if~}}
|
||||
{{#if field_vector}}to_cpp_{{field_cpp_type}}_vec(dto->{{field_name}}, dto->{{field_name}}Count),{{/if}}
|
||||
{{/each}}
|
||||
});
|
||||
}
|
||||
|
||||
static inline std::vector<{{struct_rust_name}}> to_cpp_{{struct_rust_name}}_vec(const {{struct_c_name}}* const* arr, size_t c) {
|
||||
if (arr == nullptr || c < 1) { return std::vector<{{struct_rust_name}}>(); }
|
||||
std::vector<{{struct_rust_name}}> result;
|
||||
result.reserve(c);
|
||||
for (size_t i = 0; i < c; ++i) {
|
||||
auto dto = arr[i];
|
||||
if (dto == nullptr) { continue; }
|
||||
result.push_back(to_cpp_{{struct_rust_name}}(dto).value());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline {{struct_c_name}}* alloc_c_{{struct_rust_name}}(const {{struct_rust_name}}* dto) {
|
||||
if (dto == nullptr) { return nullptr; }
|
||||
{{struct_c_name}}* obj = new {{struct_c_name}}{};
|
||||
{{#each fields}}
|
||||
{{#if field_primitive}}obj->{{field_name}} = dto->{{field_name}};{{/if~}}
|
||||
{{#if field_normal}}obj->{{field_name}} = alloc_c_{{field_cpp_type}}(dto->{{field_name}});{{/if~}}
|
||||
{{#if field_vector}}obj->{{field_name}} = alloc_c_{{field_cpp_type}}_vec(dto->{{field_name}}, &obj->{{field_name}}Count);{{/if}}
|
||||
{{/each}}
|
||||
return obj;
|
||||
}
|
||||
|
||||
static inline {{struct_c_name}}* alloc_c_{{struct_rust_name}}(const std::optional<{{struct_rust_name}}>& dto) {
|
||||
if (!dto.has_value()) { return nullptr; }
|
||||
return alloc_c_{{struct_rust_name}}(dto.value());
|
||||
}
|
||||
|
||||
static inline {{struct_c_name}}** alloc_c_{{struct_rust_name}}_vec(const std::vector<{{struct_rust_name}}>& dto, size_t* count) {
|
||||
if (dto.empty()) {
|
||||
*count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
*count = dto.size();
|
||||
{{struct_c_name}}** arr = new {{struct_c_name}}*[*count];
|
||||
for (size_t i = 0; i < *count; ++i) {
|
||||
arr[i] = alloc_c_{{struct_rust_name}}(dto[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
static inline void free_c_{{struct_rust_name}}({{struct_c_name}}* obj) {
|
||||
if (obj == nullptr) { return; }
|
||||
{{#each fields}}
|
||||
{{#if field_normal}}free_c_{{field_cpp_type}}(obj->{{field_name}});{{/if~}}
|
||||
{{#if field_vector}}free_c_{{field_cpp_type}}_vec(obj->{{field_name}}, obj->{{field_name}}Count);{{/if}}
|
||||
{{/each}}
|
||||
delete obj;
|
||||
}
|
||||
|
||||
static inline void free_c_{{struct_rust_name}}_vec({{struct_c_name}}** arr, size_t count) {
|
||||
if (arr == nullptr || count < 1) { return; }
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
free_c_{{struct_rust_name}}(arr[i]);
|
||||
}
|
||||
delete[] arr;
|
||||
}
|
||||
{{/each}}
|
||||
@@ -4,7 +4,7 @@
|
||||
{{rust_comment}}
|
||||
pub struct {{struct_c_name}} {
|
||||
{{#each fields}}
|
||||
{{rust_comment}}
|
||||
{{#indent "rust_comment" " "}}
|
||||
pub {{field_name}}: {{#unless field_primitive}}*mut {{/unless}}{{~#if field_vector}}*mut {{/if}}{{field_c_type}},
|
||||
{{#if field_vector}}
|
||||
/// The number of elements in the {{field_name}} array.
|
||||
@@ -4,7 +4,7 @@
|
||||
{
|
||||
var body = File.ReadAllText(path);
|
||||
ReplaceTextBetween(ref body, placeholderName, text);
|
||||
File.WriteAllText(path, body);
|
||||
File.WriteAllText(path, body.ReplaceLineEndings("\n"));
|
||||
}
|
||||
|
||||
public static void ReplaceTextBetween(ref string body, string placeholderName, string text)
|
||||
@@ -31,4 +31,18 @@
|
||||
var lines = text.ReplaceLineEndings("\n").Split(['\n']).Select(l => prefix + l);
|
||||
return String.Join("\n", lines);
|
||||
}
|
||||
|
||||
public static string ToRustComment(this string text)
|
||||
{
|
||||
return text.PrefixEveryLine("/// ");
|
||||
}
|
||||
|
||||
public static string ToCppComment(this string text)
|
||||
{
|
||||
if (text.Contains("\n")) {
|
||||
return "/**\n" + text.PrefixEveryLine(" * ") + "\n */";
|
||||
} else {
|
||||
return $"/** {text} */";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Nullable>disable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -19,8 +19,4 @@
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Templates\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user