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,259 +128,302 @@ VPKC_EXPORT void VPKC_CALL vpkc_free_asset(vpkc_asset_t* pAsset); | ||||
| #ifdef __cplusplus | ||||
|  | ||||
| namespace Velopack { | ||||
|     struct VelopackAsset { | ||||
|         std::string PackageId; | ||||
|         std::string Version; | ||||
|         std::string Type; | ||||
|         std::string FileName; | ||||
|         std::string SHA1; | ||||
|         std::string SHA256; | ||||
|         uint64_t Size; | ||||
|         std::string NotesMarkdown; | ||||
|         std::string NotesHtml; | ||||
|     }; | ||||
|  | ||||
|     struct UpdateInfo { | ||||
|         VelopackAsset TargetFullRelease; | ||||
|         bool IsDowngrade; | ||||
|     }; | ||||
| static inline void throw_last_error() { | ||||
|     size_t neededSize = vpkc_get_last_error(nullptr, 0); | ||||
|     std::string strError(neededSize, '\0'); | ||||
|     vpkc_get_last_error(&strError[0], neededSize); | ||||
|     throw std::runtime_error(strError); | ||||
| } | ||||
|  | ||||
|     struct UpdateOptions { | ||||
|         bool AllowVersionDowngrade; | ||||
|         std::string ExplicitChannel; | ||||
|     }; | ||||
| static inline std::string to_cppstring(const char* psz) { | ||||
|     return psz == nullptr ? "" : psz; | ||||
| } | ||||
|  | ||||
|     struct VelopackLocator { | ||||
|         std::string RootAppDir; | ||||
|         std::string UpdateExePath; | ||||
|         std::string PackagesDir; | ||||
|         std::string ManifestPath; | ||||
|         std::string CurrentBinaryDir; | ||||
|         bool IsPortable; | ||||
|     }; | ||||
| static inline char* to_cstring(const std::string& str) { | ||||
|     return const_cast<char*>(str.c_str()); | ||||
| } | ||||
|  | ||||
|     static inline void throw_last_error() { | ||||
|         size_t neededSize = vpkc_get_last_error(nullptr, 0); | ||||
|         std::string strError(neededSize, '\0'); | ||||
|         vpkc_get_last_error(&strError[0], neededSize); | ||||
|         throw std::runtime_error(strError); | ||||
|     } | ||||
| static inline char* to_cstring_opt(const std::optional<std::string>& str) { | ||||
|     return str.has_value() ? to_cstring(str.value()) : nullptr; | ||||
| } | ||||
|  | ||||
|     static inline vpkc_locator_t to_vpkc(const VelopackLocator& locator) { | ||||
|         return { | ||||
|             const_cast<char*>(locator.RootAppDir.c_str()), | ||||
|             const_cast<char*>(locator.UpdateExePath.c_str()), | ||||
|             const_cast<char*>(locator.PackagesDir.c_str()), | ||||
|             const_cast<char*>(locator.ManifestPath.c_str()), | ||||
|             const_cast<char*>(locator.CurrentBinaryDir.c_str()), | ||||
|             locator.IsPortable | ||||
|         }; | ||||
|     } | ||||
| static inline std::optional<std::string> to_cppstring_opt(const char* psz) { | ||||
|     return psz == nullptr ? std::nullopt : std::optional<std::string>(psz); | ||||
| } | ||||
|  | ||||
|     static inline vpkc_options_t to_vpkc(const UpdateOptions& options) { | ||||
|         return { | ||||
|             options.AllowVersionDowngrade, | ||||
|             const_cast<char*>(options.ExplicitChannel.c_str()) | ||||
|         }; | ||||
|     } | ||||
| static inline bool to_cppbool(bool b) { return b; } | ||||
| static inline bool to_cbool(bool b) { return b; } | ||||
| static inline uint64_t to_cu64(uint64_t i) { return i; } | ||||
| static inline uint64_t to_cppu64(uint64_t i) { return i; } | ||||
|  | ||||
|     static inline vpkc_asset_t to_vpkc(const VelopackAsset& asset) { | ||||
|         return { | ||||
|             const_cast<char*>(asset.PackageId.c_str()), | ||||
|             const_cast<char*>(asset.Version.c_str()), | ||||
|             const_cast<char*>(asset.Type.c_str()), | ||||
|             const_cast<char*>(asset.FileName.c_str()), | ||||
|             const_cast<char*>(asset.SHA1.c_str()), | ||||
|             const_cast<char*>(asset.SHA256.c_str()), | ||||
|             asset.Size, | ||||
|             const_cast<char*>(asset.NotesMarkdown.c_str()), | ||||
|             const_cast<char*>(asset.NotesHtml.c_str()) | ||||
|         }; | ||||
|     } | ||||
| // !! AUTO-GENERATED-START CPP_TYPES | ||||
| struct VelopackLocatorConfig { | ||||
|     std::string RootAppDir; | ||||
|     std::string UpdateExePath; | ||||
|     std::string PackagesDir; | ||||
|     std::string ManifestPath; | ||||
|     std::string CurrentBinaryDir; | ||||
|     bool IsPortable; | ||||
| }; | ||||
|  | ||||
|     static inline VelopackAsset from_vpkc(const vpkc_asset_t& asset) { | ||||
|         return { | ||||
|             asset.PackageId, | ||||
|             asset.Version, | ||||
|             asset.Type, | ||||
|             asset.FileName, | ||||
|             asset.SHA1, | ||||
|             asset.SHA256, | ||||
|             asset.Size, | ||||
|             asset.NotesMarkdown, | ||||
|             asset.NotesHtml | ||||
|         }; | ||||
|     } | ||||
|      | ||||
|     static inline vpkc_update_info_t to_vpkc(const UpdateInfo& update) { | ||||
|         return { | ||||
|             to_vpkc(update.TargetFullRelease), | ||||
|             update.IsDowngrade | ||||
|         }; | ||||
|     } | ||||
|      | ||||
|     static inline UpdateInfo from_vpkc(const vpkc_update_info_t& update) { | ||||
|         return { | ||||
|             from_vpkc(update.TargetFullRelease), | ||||
|             update.IsDowngrade | ||||
|         }; | ||||
|     } | ||||
|      | ||||
|     class VelopackApp { | ||||
|     private: | ||||
|         VelopackApp(); | ||||
|     public: | ||||
|         static VelopackApp Build() {  | ||||
|             return VelopackApp();  | ||||
|         }; | ||||
|         VelopackApp& SetAutoApplyOnStartup(bool bAutoApply) { | ||||
|             vpkc_app_set_auto_apply_on_startup(bAutoApply); | ||||
|             return *this; | ||||
|         }; | ||||
|         VelopackApp& SetArgs(const std::vector<std::string>& args) { | ||||
|             char** pArgs = new char*[args.size()]; | ||||
|             for (size_t i = 0; i < args.size(); i++) { | ||||
|                 pArgs[i] = new char[args[i].size() + 1]; | ||||
|                 strcpy_s(pArgs[i], args[i].size() + 1, args[i].c_str()); | ||||
|             } | ||||
|             vpkc_app_set_args(pArgs, args.size()); | ||||
|              | ||||
|             // Free all the memory | ||||
|             for (size_t i = 0; i < args.size(); i++) { | ||||
|                 delete[] pArgs[i]; | ||||
|             } | ||||
|             delete[] pArgs; | ||||
|             return *this; | ||||
|         }; | ||||
|         VelopackApp& SetLocator(const VelopackLocator& locator) { | ||||
|             vpkc_locator_t vpkc_locator = to_vpkc(locator); | ||||
|             vpkc_app_set_locator(const_cast<vpkc_locator_t*>(&vpkc_locator)); | ||||
|             return *this; | ||||
|         }; | ||||
|         VelopackApp& OnAfterInstall(vpkc_hook_callback_t cbInstall) { | ||||
|             vpkc_app_set_hook_after_install(cbInstall); | ||||
|             return *this; | ||||
|         }; | ||||
|         VelopackApp& OnBeforeUninstall(vpkc_hook_callback_t cbInstall) { | ||||
|             vpkc_app_set_hook_before_uninstall(cbInstall); | ||||
|             return *this; | ||||
|         }; | ||||
|         VelopackApp& OnBeforeUpdate(vpkc_hook_callback_t cbInstall) { | ||||
|             vpkc_app_set_hook_before_update(cbInstall); | ||||
|             return *this; | ||||
|         }; | ||||
|         VelopackApp& OnAfterUpdate(vpkc_hook_callback_t cbInstall) { | ||||
|             vpkc_app_set_hook_after_update(cbInstall); | ||||
|             return *this; | ||||
|         }; | ||||
|         VelopackApp& OnFirstRun(vpkc_hook_callback_t cbInstall) { | ||||
|             vpkc_app_set_hook_first_run(cbInstall); | ||||
|             return *this; | ||||
|         }; | ||||
|         VelopackApp& OnRestarted(vpkc_hook_callback_t cbInstall) { | ||||
|             vpkc_app_set_hook_restarted(cbInstall); | ||||
|             return *this; | ||||
|         }; | ||||
|         void Run() { | ||||
|             vpkc_app_run(); | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     class UpdateManager { | ||||
|     private: | ||||
|         vpkc_update_manager_t m_pManager; | ||||
|     public: | ||||
|         UpdateManager(const std::string& urlOrPath, const UpdateOptions* options, const VelopackLocator* locator) { | ||||
|             vpkc_options_t* pOptions = nullptr; | ||||
|             if (options != nullptr) { | ||||
|                 vpkc_options_t vpkc_options = to_vpkc(*options); | ||||
|                 pOptions = const_cast<vpkc_options_t*>(&vpkc_options); | ||||
|             } | ||||
|              | ||||
|             vpkc_locator_t* pLocator = nullptr; | ||||
|             if (locator != nullptr) { | ||||
|                 vpkc_locator_t vpkc_locator = to_vpkc(*locator); | ||||
|                 pLocator = const_cast<vpkc_locator_t*>(&vpkc_locator); | ||||
|             } | ||||
|              | ||||
|             if (0 != vpkc_new_update_manager(urlOrPath.c_str(), pOptions, pLocator, &m_pManager)) { | ||||
|                 throw_last_error(); | ||||
|             } | ||||
|         }; | ||||
|         ~UpdateManager() { | ||||
|             vpkc_free_update_manager(&m_pManager); | ||||
|         }; | ||||
|         bool IsPortable() noexcept { | ||||
|             return vpkc_is_portable(&m_pManager); | ||||
|         }; | ||||
|         std::string GetCurrentVersion() noexcept { | ||||
|             size_t neededSize = vpkc_get_current_version(&m_pManager, nullptr, 0); | ||||
|             std::string strVersion(neededSize, '\0'); | ||||
|             vpkc_get_current_version(&m_pManager, &strVersion[0], neededSize); | ||||
|             return strVersion; | ||||
|         }; | ||||
|         std::string GetAppId() noexcept { | ||||
|             size_t neededSize = vpkc_get_app_id(&m_pManager, nullptr, 0); | ||||
|             std::string strId(neededSize, '\0'); | ||||
|             vpkc_get_app_id(&m_pManager, &strId[0], neededSize); | ||||
|             return strId; | ||||
|         }; | ||||
|         std::optional<VelopackAsset> UpdatePendingRestart() noexcept { | ||||
|             vpkc_asset_t asset; | ||||
|             if (vpkc_update_pending_restart(&m_pManager, &asset)) { | ||||
|                 VelopackAsset cpp_asset = from_vpkc(asset); | ||||
|                 vpkc_free_asset(&asset); | ||||
|                 return cpp_asset; | ||||
|             } | ||||
|             return std::nullopt; | ||||
|         }; | ||||
|         std::optional<UpdateInfo> CheckForUpdates() { | ||||
|             vpkc_update_info_t update; | ||||
|             vpkc_update_check_t result = vpkc_check_for_updates(&m_pManager, &update); | ||||
|             switch (result) { | ||||
|                 case vpkc_update_check_t::ERROR: | ||||
|                     throw_last_error(); | ||||
|                     return std::nullopt; | ||||
|                 case vpkc_update_check_t::NO_UPDATE_AVAILABLE: | ||||
|                     return std::nullopt; | ||||
|                 case vpkc_update_check_t::UPDATE_AVAILABLE: | ||||
|                     UpdateInfo cpp_info = from_vpkc(update); | ||||
|                     vpkc_free_update_info(&update); | ||||
|                     return cpp_info; | ||||
|             } | ||||
|         }; | ||||
|         void DownloadUpdates(const UpdateInfo& update, vpkc_progress_callback_t progress) { | ||||
|             vpkc_update_info_t vpkc_update = to_vpkc(update); | ||||
|             if (!vpkc_download_updates(&m_pManager, &vpkc_update, progress)) { | ||||
|                 throw_last_error(); | ||||
|             } | ||||
|         }; | ||||
|         void WaitExitThenApplyUpdate(const VelopackAsset& asset, bool silent, bool restart, std::vector<std::string> restartArgs) { | ||||
|             char** pRestartArgs = new char*[restartArgs.size()]; | ||||
|             for (size_t i = 0; i < restartArgs.size(); i++) { | ||||
|                 pRestartArgs[i] = new char[restartArgs[i].size() + 1]; | ||||
|                 strcpy_s(pRestartArgs[i], restartArgs[i].size() + 1, restartArgs[i].c_str()); | ||||
|             } | ||||
|              | ||||
|             vpkc_asset_t vpkc_asset = to_vpkc(asset); | ||||
|             bool result = vpkc_wait_exit_then_apply_update(&m_pManager, &vpkc_asset, silent, restart, pRestartArgs, restartArgs.size()); | ||||
|              | ||||
|             // Free all the memory | ||||
|             for (size_t i = 0; i < restartArgs.size(); i++) { | ||||
|                 delete[] pRestartArgs[i]; | ||||
|             } | ||||
|             delete[] pRestartArgs; | ||||
|              | ||||
|             if (!result) { | ||||
|                 throw_last_error(); | ||||
|             } | ||||
|         }; | ||||
|         void WaitExitThenApplyUpdate(const UpdateInfo& asset, bool silent, bool restart, std::vector<std::string> restartArgs) { | ||||
|             this->WaitExitThenApplyUpdate(asset.TargetFullRelease, silent, restart, restartArgs); | ||||
|         }; | ||||
| static inline vpkc_locator_config_t to_c(const VelopackLocatorConfig& dto) { | ||||
|     return { | ||||
|         to_cstring(dto.RootAppDir), | ||||
|         to_cstring(dto.UpdateExePath), | ||||
|         to_cstring(dto.PackagesDir), | ||||
|         to_cstring(dto.ManifestPath), | ||||
|         to_cstring(dto.CurrentBinaryDir), | ||||
|         to_cbool(dto.IsPortable), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| static inline VelopackLocatorConfig to_cpp(const vpkc_locator_config_t& dto) { | ||||
|     return { | ||||
|         to_cppstring(dto.RootAppDir), | ||||
|         to_cppstring(dto.UpdateExePath), | ||||
|         to_cppstring(dto.PackagesDir), | ||||
|         to_cppstring(dto.ManifestPath), | ||||
|         to_cppstring(dto.CurrentBinaryDir), | ||||
|         to_cppbool(dto.IsPortable), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| struct VelopackAsset { | ||||
|     std::string PackageId; | ||||
|     std::string Version; | ||||
|     std::string Type; | ||||
|     std::string FileName; | ||||
|     std::string SHA1; | ||||
|     std::string SHA256; | ||||
|     uint64_t Size; | ||||
|     std::string NotesMarkdown; | ||||
|     std::string NotesHtml; | ||||
| }; | ||||
|  | ||||
| static inline vpkc_asset_t to_c(const VelopackAsset& dto) { | ||||
|     return { | ||||
|         to_cstring(dto.PackageId), | ||||
|         to_cstring(dto.Version), | ||||
|         to_cstring(dto.Type), | ||||
|         to_cstring(dto.FileName), | ||||
|         to_cstring(dto.SHA1), | ||||
|         to_cstring(dto.SHA256), | ||||
|         to_cu64(dto.Size), | ||||
|         to_cstring(dto.NotesMarkdown), | ||||
|         to_cstring(dto.NotesHtml), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| static inline VelopackAsset to_cpp(const vpkc_asset_t& dto) { | ||||
|     return { | ||||
|         to_cppstring(dto.PackageId), | ||||
|         to_cppstring(dto.Version), | ||||
|         to_cppstring(dto.Type), | ||||
|         to_cppstring(dto.FileName), | ||||
|         to_cppstring(dto.SHA1), | ||||
|         to_cppstring(dto.SHA256), | ||||
|         to_cppu64(dto.Size), | ||||
|         to_cppstring(dto.NotesMarkdown), | ||||
|         to_cppstring(dto.NotesHtml), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| struct UpdateInfo { | ||||
|     VelopackAsset TargetFullRelease; | ||||
|     bool IsDowngrade; | ||||
| }; | ||||
|  | ||||
| static inline vpkc_update_info_t to_c(const UpdateInfo& dto) { | ||||
|     return { | ||||
|         to_c(dto.TargetFullRelease), | ||||
|         to_cbool(dto.IsDowngrade), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| static inline UpdateInfo to_cpp(const vpkc_update_info_t& dto) { | ||||
|     return { | ||||
|         to_cpp(dto.TargetFullRelease), | ||||
|         to_cppbool(dto.IsDowngrade), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| struct UpdateOptions { | ||||
|     bool AllowVersionDowngrade; | ||||
|     std::optional<std::string> ExplicitChannel; | ||||
| }; | ||||
|  | ||||
| static inline vpkc_update_options_t to_c(const UpdateOptions& dto) { | ||||
|     return { | ||||
|         to_cbool(dto.AllowVersionDowngrade), | ||||
|         to_cstring_opt(dto.ExplicitChannel), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| static inline UpdateOptions to_cpp(const vpkc_update_options_t& dto) { | ||||
|     return { | ||||
|         to_cppbool(dto.AllowVersionDowngrade), | ||||
|         to_cppstring_opt(dto.ExplicitChannel), | ||||
|     }; | ||||
| } | ||||
| // !! AUTO-GENERATED-END CPP_TYPES | ||||
|  | ||||
| class VelopackApp { | ||||
| private: | ||||
|     VelopackApp(); | ||||
| public: | ||||
|     static VelopackApp Build() {  | ||||
|         return VelopackApp();  | ||||
|     }; | ||||
|     VelopackApp& SetAutoApplyOnStartup(bool bAutoApply) { | ||||
|         vpkc_app_set_auto_apply_on_startup(bAutoApply); | ||||
|         return *this; | ||||
|     }; | ||||
|     VelopackApp& SetArgs(const std::vector<std::string>& args) { | ||||
|         char** pArgs = new char*[args.size()]; | ||||
|         for (size_t i = 0; i < args.size(); i++) { | ||||
|             pArgs[i] = new char[args[i].size() + 1]; | ||||
|             strcpy_s(pArgs[i], args[i].size() + 1, args[i].c_str()); | ||||
|         } | ||||
|         vpkc_app_set_args(pArgs, args.size()); | ||||
|          | ||||
|         // Free all the memory | ||||
|         for (size_t i = 0; i < args.size(); i++) { | ||||
|             delete[] pArgs[i]; | ||||
|         } | ||||
|         delete[] pArgs; | ||||
|         return *this; | ||||
|     }; | ||||
|     VelopackApp& SetLocator(const VelopackLocatorConfig& locator) { | ||||
|         vpkc_locator_config_t vpkc_locator = to_c(locator); | ||||
|         vpkc_app_set_locator(&vpkc_locator); | ||||
|         return *this; | ||||
|     }; | ||||
|     VelopackApp& OnAfterInstall(vpkc_hook_callback_t cbInstall) { | ||||
|         vpkc_app_set_hook_after_install(cbInstall); | ||||
|         return *this; | ||||
|     }; | ||||
|     VelopackApp& OnBeforeUninstall(vpkc_hook_callback_t cbInstall) { | ||||
|         vpkc_app_set_hook_before_uninstall(cbInstall); | ||||
|         return *this; | ||||
|     }; | ||||
|     VelopackApp& OnBeforeUpdate(vpkc_hook_callback_t cbInstall) { | ||||
|         vpkc_app_set_hook_before_update(cbInstall); | ||||
|         return *this; | ||||
|     }; | ||||
|     VelopackApp& OnAfterUpdate(vpkc_hook_callback_t cbInstall) { | ||||
|         vpkc_app_set_hook_after_update(cbInstall); | ||||
|         return *this; | ||||
|     }; | ||||
|     VelopackApp& OnFirstRun(vpkc_hook_callback_t cbInstall) { | ||||
|         vpkc_app_set_hook_first_run(cbInstall); | ||||
|         return *this; | ||||
|     }; | ||||
|     VelopackApp& OnRestarted(vpkc_hook_callback_t cbInstall) { | ||||
|         vpkc_app_set_hook_restarted(cbInstall); | ||||
|         return *this; | ||||
|     }; | ||||
|     void Run() { | ||||
|         vpkc_app_run(); | ||||
|     }; | ||||
| }; | ||||
|  | ||||
| class UpdateManager { | ||||
| private: | ||||
|     vpkc_update_manager_t m_pManager; | ||||
| public: | ||||
|     UpdateManager(const std::string& urlOrPath, const UpdateOptions* options = nullptr, const VelopackLocatorConfig* locator = nullptr) { | ||||
|         vpkc_update_options_t* pOptions = nullptr; | ||||
|         if (options != nullptr) { | ||||
|             vpkc_update_options_t vpkc_options = to_c(*options); | ||||
|             pOptions = &vpkc_options; | ||||
|         } | ||||
|          | ||||
|         vpkc_locator_config_t* pLocator = nullptr; | ||||
|         if (locator != nullptr) { | ||||
|             vpkc_locator_config_t vpkc_locator = to_c(*locator); | ||||
|             pLocator = &vpkc_locator; | ||||
|         } | ||||
|          | ||||
|         if (0 != vpkc_new_update_manager(urlOrPath.c_str(), pOptions, pLocator, &m_pManager)) { | ||||
|             throw_last_error(); | ||||
|         } | ||||
|     }; | ||||
|     ~UpdateManager() { | ||||
|         vpkc_free_update_manager(&m_pManager); | ||||
|     }; | ||||
|     bool IsPortable() noexcept { | ||||
|         return vpkc_is_portable(&m_pManager); | ||||
|     }; | ||||
|     std::string GetCurrentVersion() noexcept { | ||||
|         size_t neededSize = vpkc_get_current_version(&m_pManager, nullptr, 0); | ||||
|         std::string strVersion(neededSize, '\0'); | ||||
|         vpkc_get_current_version(&m_pManager, &strVersion[0], neededSize); | ||||
|         return strVersion; | ||||
|     }; | ||||
|     std::string GetAppId() noexcept { | ||||
|         size_t neededSize = vpkc_get_app_id(&m_pManager, nullptr, 0); | ||||
|         std::string strId(neededSize, '\0'); | ||||
|         vpkc_get_app_id(&m_pManager, &strId[0], neededSize); | ||||
|         return strId; | ||||
|     }; | ||||
|     std::optional<VelopackAsset> UpdatePendingRestart() noexcept { | ||||
|         vpkc_asset_t asset; | ||||
|         if (vpkc_update_pending_restart(&m_pManager, &asset)) { | ||||
|             VelopackAsset cpp_asset = to_cpp(asset); | ||||
|             vpkc_free_asset(&asset); | ||||
|             return cpp_asset; | ||||
|         } | ||||
|         return std::nullopt; | ||||
|     }; | ||||
|     std::optional<UpdateInfo> CheckForUpdates() { | ||||
|         vpkc_update_info_t update; | ||||
|         vpkc_update_check_t result = vpkc_check_for_updates(&m_pManager, &update); | ||||
|         switch (result) { | ||||
|             case vpkc_update_check_t::ERROR: | ||||
|                 throw_last_error(); | ||||
|                 return std::nullopt; | ||||
|             case vpkc_update_check_t::NO_UPDATE_AVAILABLE: | ||||
|                 return std::nullopt; | ||||
|             case vpkc_update_check_t::UPDATE_AVAILABLE: | ||||
|                 UpdateInfo cpp_info = to_cpp(update); | ||||
|                 vpkc_free_update_info(&update); | ||||
|                 return cpp_info; | ||||
|         } | ||||
|     }; | ||||
|     void DownloadUpdates(const UpdateInfo& update, vpkc_progress_callback_t progress) { | ||||
|         vpkc_update_info_t vpkc_update = to_c(update); | ||||
|         if (!vpkc_download_updates(&m_pManager, &vpkc_update, progress)) { | ||||
|             throw_last_error(); | ||||
|         } | ||||
|     }; | ||||
|     void WaitExitThenApplyUpdate(const VelopackAsset& asset, bool silent, bool restart, std::vector<std::string> restartArgs) { | ||||
|         char** pRestartArgs = new char*[restartArgs.size()]; | ||||
|         for (size_t i = 0; i < restartArgs.size(); i++) { | ||||
|             pRestartArgs[i] = new char[restartArgs[i].size() + 1]; | ||||
|             strcpy_s(pRestartArgs[i], restartArgs[i].size() + 1, restartArgs[i].c_str()); | ||||
|         } | ||||
|          | ||||
|         vpkc_asset_t vpkc_asset = to_c(asset); | ||||
|         bool result = vpkc_wait_exit_then_apply_update(&m_pManager, &vpkc_asset, silent, restart, pRestartArgs, restartArgs.size()); | ||||
|          | ||||
|         // Free all the memory | ||||
|         for (size_t i = 0; i < restartArgs.size(); i++) { | ||||
|             delete[] pRestartArgs[i]; | ||||
|         } | ||||
|         delete[] pRestartArgs; | ||||
|          | ||||
|         if (!result) { | ||||
|             throw_last_error(); | ||||
|         } | ||||
|     }; | ||||
|     void WaitExitThenApplyUpdate(const UpdateInfo& asset, bool silent, bool restart, std::vector<std::string> restartArgs) { | ||||
|         this->WaitExitThenApplyUpdate(asset.TargetFullRelease, silent, restart, restartArgs); | ||||
|     }; | ||||
| }; | ||||
|  | ||||
| } // namespace Velopack | ||||
|  | ||||
| #endif // __cplusplus | ||||
|  | ||||
| #endif // VELOPACK_H | ||||
| @@ -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))?; | ||||
|         Ok(Box::new(UpdateManagerOpaque { obj: update_manager })) | ||||
|     } else { | ||||
|         let update_manager = UpdateManager::new(source, Some(update_options), None)?; | ||||
|         Ok(Box::new(UpdateManagerOpaque { obj: update_manager })) | ||||
|     } | ||||
|     let options = updateoptions_to_core_option(options); | ||||
|     let locator = velopacklocatorconfig_to_core_option(locator); | ||||
|     let update_manager = UpdateManager::new(source, options, locator)?; | ||||
|     Ok(Box::new(UpdateManagerOpaque { obj: update_manager })) | ||||
| } | ||||
|  | ||||
| fn bridge_get_current_version(manager: &UpdateManagerOpaque) -> String { | ||||
| @@ -197,29 +168,22 @@ fn bridge_is_portable(manager: &UpdateManagerOpaque) -> bool { | ||||
|     manager.obj.get_is_portable() | ||||
| } | ||||
|  | ||||
| fn bridge_update_pending_restart(manager: &UpdateManagerOpaque) -> ffi::AssetOption { | ||||
|     if let Some(info) = manager.obj.get_update_pending_restart() { | ||||
|         let update = from_asset(&info); | ||||
|         ffi::AssetOption { data: update, has_data: true } | ||||
|     } else { | ||||
|         let default = ffi::AssetDto::default(); | ||||
|         ffi::AssetOption { data: default, has_data: false } | ||||
|     } | ||||
| fn bridge_update_pending_restart(manager: &UpdateManagerOpaque) -> ffi::VelopackAssetDtoOption { | ||||
|     let asset_opt = manager.obj.get_update_pending_restart(); | ||||
|     velopackasset_to_bridge_option(&asset_opt) | ||||
| } | ||||
|  | ||||
| fn bridge_check_for_updates(manager: &UpdateManagerOpaque) -> Result<ffi::UpdateInfoOption> { | ||||
|     if let UpdateCheck::UpdateAvailable(info) = manager.obj.check_for_updates()? { | ||||
|         let update = from_update_info(&info); | ||||
|         Ok(ffi::UpdateInfoOption { data: update, has_data: true }) | ||||
|     } else { | ||||
|         let default = ffi::UpdateInfoDto::default(); | ||||
|         Ok(ffi::UpdateInfoOption { data: default, has_data: false }) | ||||
|     } | ||||
| fn bridge_check_for_updates(manager: &UpdateManagerOpaque) -> Result<ffi::UpdateInfoDtoOption> { | ||||
|     let info_opt = if let UpdateCheck::UpdateAvailable(info) = manager.obj.check_for_updates()? { Some(info) } else { None }; | ||||
|     Ok(updateinfo_to_bridge_option(&info_opt)) | ||||
| } | ||||
|  | ||||
| fn bridge_download_updates(manager: &UpdateManagerOpaque, to_download: &ffi::UpdateInfoDto, cb: &ffi::DownloadCallbackManager) -> Result<()> { | ||||
|     let info = to_update_info(&to_download); | ||||
|  | ||||
| fn bridge_download_updates( | ||||
|     manager: &UpdateManagerOpaque, | ||||
|     to_download: &ffi::UpdateInfoDto, | ||||
|     cb: &ffi::DownloadCallbackManager, | ||||
| ) -> Result<()> { | ||||
|     let info = updateinfo_to_core(&to_download); | ||||
|     let (progress_sender, progress_receiver) = std::sync::mpsc::channel::<i16>(); | ||||
|     let (completion_sender, completion_receiver) = std::sync::mpsc::channel::<std::result::Result<(), VelopackError>>(); | ||||
|  | ||||
| @@ -260,13 +224,24 @@ fn bridge_download_updates(manager: &UpdateManagerOpaque, to_download: &ffi::Upd | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn bridge_wait_exit_then_apply_update(manager: &UpdateManagerOpaque, to_apply: &ffi::AssetDto, silent: bool, restart: bool, restart_args: &Vec<String>) -> Result<()> { | ||||
|     let asset = to_asset(&to_apply); | ||||
| fn bridge_wait_exit_then_apply_update( | ||||
|     manager: &UpdateManagerOpaque, | ||||
|     to_apply: &ffi::VelopackAssetDto, | ||||
|     silent: bool, | ||||
|     restart: bool, | ||||
|     restart_args: &Vec<String>, | ||||
| ) -> Result<()> { | ||||
|     let asset = velopackasset_to_core(&to_apply); | ||||
|     manager.obj.wait_exit_then_apply_updates(&asset, silent, restart, restart_args)?; | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| fn bridge_appbuilder_run(cb: &ffi::HookCallbackManager, custom_args: &ffi::StringArrayOption, locator: &ffi::LocatorConfigOption, auto_apply: bool) { | ||||
| fn bridge_appbuilder_run( | ||||
|     cb: &ffi::HookCallbackManager, | ||||
|     custom_args: &ffi::StringArrayOption, | ||||
|     locator: &ffi::VelopackLocatorConfigDtoOption, | ||||
|     auto_apply: bool, | ||||
| ) { | ||||
|     let mut app = VelopackApp::build() | ||||
|         .set_auto_apply_on_startup(auto_apply) | ||||
|         .on_first_run(|v| cb.firstrun_hook(v.to_string())) | ||||
| @@ -282,7 +257,8 @@ fn bridge_appbuilder_run(cb: &ffi::HookCallbackManager, custom_args: &ffi::Strin | ||||
|     } | ||||
|  | ||||
|     if locator.has_data { | ||||
|         app = app.set_locator(to_locator_config(&locator.data)); | ||||
|         let locator = velopacklocatorconfig_to_core(&locator.data); | ||||
|         app = app.set_locator(locator); | ||||
|     } | ||||
|  | ||||
|     if custom_args.has_data { | ||||
| @@ -314,7 +290,8 @@ impl Log for LoggerImpl { | ||||
|             Level::Info => "info", | ||||
|             Level::Debug => "debug", | ||||
|             Level::Trace => "trace", | ||||
|         }.to_string(); | ||||
|         } | ||||
|         .to_string(); | ||||
|  | ||||
|         if let Some(cb) = get_logger() { | ||||
|             if let Some(cb) = unsafe { cb.as_mut() } { | ||||
|   | ||||
							
								
								
									
										162
									
								
								src/lib-cpp/src/map.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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