diff --git a/src/code-generator/Program.cs b/src/code-generator/Program.cs index 07948bba..6e5c70c8 100644 --- a/src/code-generator/Program.cs +++ b/src/code-generator/Program.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using HandlebarsDotNet; var scriptsDir = Assembly.GetEntryAssembly()! @@ -98,6 +98,12 @@ Console.WriteLine("Writing all to file"); Util.ReplaceTextInFile(rustTypes, "RUST_TYPES", rustCTypes.ToString().ReplaceLineEndings("\n")); Util.ReplaceTextInFile(rustCppInclude, "CPP_TYPES", cppTypes.ToString().ReplaceLineEndings("\n")); +// --- Python asset.rs generation --- +string pythonAssetRs = Path.Combine(scriptsDir, "..", "lib-python", "src", "asset.rs"); +var pythonAssetTemplate = Handlebars.Compile(File.ReadAllText(Path.Combine(templatesDir, "python_asset.hbs"))); +var pythonAsset = pythonAssetTemplate(handlebarData); +File.WriteAllText(pythonAssetRs, pythonAsset.ToString().ReplaceLineEndings("\n")); + return 0; class TypeMap @@ -166,6 +172,7 @@ class RustStruct_Field public bool field_vector; public bool field_system; public bool field_normal; + public bool field_primitive_or_system => field_primitive || field_system; public string rust_comment; public string cpp_comment; } \ No newline at end of file diff --git a/src/code-generator/Templates/python_asset.hbs b/src/code-generator/Templates/python_asset.hbs new file mode 100644 index 00000000..8044cdfa --- /dev/null +++ b/src/code-generator/Templates/python_asset.hbs @@ -0,0 +1,62 @@ +// This file is auto-generated. Do not edit by hand. +#![allow(non_snake_case)] +use pyo3::prelude::*; +use velopack::{VelopackAsset, UpdateInfo, UpdateOptions, locator::VelopackLocatorConfig}; +use std::path::PathBuf; + +{{#each this}} +#[pyclass(name = "{{struct_rust_name}}")] +#[derive(Debug, Clone, Default)] +pub struct Py{{struct_rust_name}} { + {{#each fields}} + pub {{field_name}}: {{#if field_vector}}Vec<{{/if}}{{#if field_optional}}Option<{{/if~}} + {{~#unless field_primitive_or_system}}Py{{/unless}}{{field_rust_type}} + {{~#if field_optional}}>{{/if}}{{#if field_vector}}>{{/if}}, + {{/each}} +} + +impl From<{{struct_rust_name}}> for Py{{struct_rust_name}} { + fn from(value: {{struct_rust_name}}) -> Self { + Py{{struct_rust_name}} { + {{#each fields}} + {{#if field_vector}} + {{field_name}}: value.{{field_name}}.into_iter().map(Into::into).collect(), + {{/if}} + {{#if field_primitive}} + {{field_name}}: value.{{field_name}}, + {{/if}} + {{#if field_optional}} + {{field_name}}: value.{{field_name}}.map(Into::into), + {{else}} + {{#if field_normal}} + {{field_name}}: value.{{field_name}}.into(), + {{/if}} + {{/if}} + {{/each}} + } + } +} + +impl Into<{{struct_rust_name}}> for Py{{struct_rust_name}} { + fn into(self) -> {{struct_rust_name}} { + {{struct_rust_name}} { + {{#each fields}} + {{#if field_vector}} + {{field_name}}: self.{{field_name}}.into_iter().map(Into::into).collect(), + {{/if}} + {{#if field_primitive}} + {{field_name}}: self.{{field_name}}, + {{/if}} + {{#if field_optional}} + {{field_name}}: self.{{field_name}}.map(Into::into), + {{else}} + {{#if field_normal}} + {{field_name}}: self.{{field_name}}.into(), + {{/if}} + {{/if}} + {{/each}} + } + } +} + +{{/each}} \ No newline at end of file diff --git a/src/lib-python/src/asset.rs b/src/lib-python/src/asset.rs deleted file mode 100644 index b0a4a5ef..00000000 --- a/src/lib-python/src/asset.rs +++ /dev/null @@ -1,205 +0,0 @@ -use pyo3::prelude::*; - -use velopack::{VelopackAsset, UpdateInfo}; - -#[pyclass(name = "Asset")] -#[derive(Debug, Clone)] -pub struct PyVelopackAsset(pub VelopackAsset); - -#[pymethods] -impl PyVelopackAsset { - #[new] - #[pyo3(signature = (package_id=String::new(), version=String::new(), type_=String::new(), file_name=String::new(), sha1=String::new(), sha256=String::new(), size=0, notes_markdown=String::new(), notes_html=String::new()))] - pub fn new( - package_id: String, - version: String, - type_: String, - file_name: String, - sha1: String, - sha256: String, - size: u64, - notes_markdown: String, - notes_html: String, - ) -> Self { - PyVelopackAsset(VelopackAsset { - PackageId: package_id, - Version: version, - Type: type_, - FileName: file_name, - SHA1: sha1, - SHA256: sha256, - Size: size, - NotesMarkdown: notes_markdown, - NotesHtml: notes_html, - }) - } - - // Direct field access - much cleaner! - #[getter] - fn package_id(&self) -> &str { - &self.0.PackageId - } - - #[getter] - fn version(&self) -> &str { - &self.0.Version - } - - #[getter] - fn type_(&self) -> &str { - &self.0.Type - } - - #[getter] - fn file_name(&self) -> &str { - &self.0.FileName - } - - #[getter] - fn sha1(&self) -> &str { - &self.0.SHA1 - } - - #[getter] - fn sha256(&self) -> &str { - &self.0.SHA256 - } - - #[getter] - fn size(&self) -> u64 { - self.0.Size - } - - #[getter] - fn notes_markdown(&self) -> &str { - &self.0.NotesMarkdown - } - - #[getter] - fn notes_html(&self) -> &str { - &self.0.NotesHtml - } - - fn __repr__(&self) -> String { - format!( - "Velopack.Asset(package_id='{}', version='{}', type='{}', file_name='{}', size={})", - self.0.PackageId, self.0.Version, self.0.Type, self.0.FileName, self.0.Size - ) - } -} - -// Conversion traits for seamless interop -impl From for PyVelopackAsset { - fn from(asset: VelopackAsset) -> Self { - PyVelopackAsset(asset) - } -} - -impl From for VelopackAsset { - fn from(py_asset: PyVelopackAsset) -> Self { - py_asset.0 - } -} - -impl AsRef for PyVelopackAsset { - fn as_ref(&self) -> &VelopackAsset { - &self.0 - } -} - -#[pyclass(name = "UpdateInfo")] -#[derive(Debug, Clone)] -pub struct PyUpdateInfo(pub UpdateInfo); - -#[pymethods] -impl PyUpdateInfo { - #[new] - #[pyo3(signature = (target_full_release, base_release=None, deltas_to_target=Vec::new(), is_downgrade=false))] - pub fn new( - target_full_release: PyVelopackAsset, - base_release: Option, - deltas_to_target: Vec, - is_downgrade: bool, - ) -> Self { - PyUpdateInfo(UpdateInfo { - TargetFullRelease: target_full_release.into(), - BaseRelease: base_release.map(Into::into), - DeltasToTarget: deltas_to_target.into_iter().map(Into::into).collect(), - IsDowngrade: is_downgrade, - }) - } - - #[staticmethod] - pub fn new_full(target: PyVelopackAsset, is_downgrade: bool) -> PyUpdateInfo { - PyUpdateInfo(UpdateInfo { - TargetFullRelease: target.into(), - BaseRelease: None, - DeltasToTarget: Vec::new(), - IsDowngrade: is_downgrade, - }) - } - - #[staticmethod] - pub fn new_delta( - target: PyVelopackAsset, - base: PyVelopackAsset, - deltas: Vec, - ) -> PyUpdateInfo { - let rust_deltas = deltas.into_iter().map(Into::into).collect(); - PyUpdateInfo(UpdateInfo { - TargetFullRelease: target.into(), - BaseRelease: Some(base.into()), - DeltasToTarget: rust_deltas, - IsDowngrade: false, - }) - } - - #[getter] - fn target_full_release(&self) -> PyVelopackAsset { - PyVelopackAsset(self.0.TargetFullRelease.clone()) - } - - #[getter] - fn base_release(&self) -> Option { - self.0.BaseRelease.clone().map(PyVelopackAsset) - } - - #[getter] - fn deltas_to_target(&self) -> Vec { - self.0.DeltasToTarget.iter().cloned().map(PyVelopackAsset).collect() - } - - #[getter] - fn is_downgrade(&self) -> bool { - self.0.IsDowngrade - } - - fn __repr__(&self) -> String { - format!( - "UpdateInfo(target_version='{}', has_base_release={}, deltas_count={}, is_downgrade={})", - self.0.TargetFullRelease.Version, - self.0.BaseRelease.is_some(), - self.0.DeltasToTarget.len(), - self.0.IsDowngrade - ) - } -} - -impl From for PyUpdateInfo { - fn from(info: UpdateInfo) -> Self { - PyUpdateInfo(info) - } -} - -impl From for UpdateInfo { - fn from(py_info: PyUpdateInfo) -> Self { - py_info.0 - } -} - -impl AsRef for PyUpdateInfo { - fn as_ref(&self) -> &UpdateInfo { - &self.0 - } -} - diff --git a/src/lib-python/src/types.rs b/src/lib-python/src/types.rs new file mode 100644 index 00000000..2e6dc6e7 --- /dev/null +++ b/src/lib-python/src/types.rs @@ -0,0 +1,148 @@ +// This file is auto-generated. Do not edit by hand. +#![allow(non_snake_case)] +use pyo3::prelude::*; +use velopack::{VelopackAsset, UpdateInfo, UpdateOptions, locator::VelopackLocatorConfig}; +use std::path::PathBuf; + +#[pyclass(name = "VelopackLocatorConfig")] +#[derive(Debug, Clone, Default)] +pub struct PyVelopackLocatorConfig { + pub RootAppDir: PathBuf, + pub UpdateExePath: PathBuf, + pub PackagesDir: PathBuf, + pub ManifestPath: PathBuf, + pub CurrentBinaryDir: PathBuf, + pub IsPortable: bool, +} + +impl From for PyVelopackLocatorConfig { + fn from(value: VelopackLocatorConfig) -> Self { + PyVelopackLocatorConfig { + RootAppDir: value.RootAppDir.into(), + UpdateExePath: value.UpdateExePath.into(), + PackagesDir: value.PackagesDir.into(), + ManifestPath: value.ManifestPath.into(), + CurrentBinaryDir: value.CurrentBinaryDir.into(), + IsPortable: value.IsPortable, + } + } +} + +impl Into for PyVelopackLocatorConfig { + fn into(self) -> VelopackLocatorConfig { + VelopackLocatorConfig { + RootAppDir: self.RootAppDir.into(), + UpdateExePath: self.UpdateExePath.into(), + PackagesDir: self.PackagesDir.into(), + ManifestPath: self.ManifestPath.into(), + CurrentBinaryDir: self.CurrentBinaryDir.into(), + IsPortable: self.IsPortable, + } + } +} + +#[pyclass(name = "VelopackAsset")] +#[derive(Debug, Clone, Default)] +pub struct PyVelopackAsset { + pub PackageId: String, + pub Version: String, + pub Type: String, + pub FileName: String, + pub SHA1: String, + pub SHA256: String, + pub Size: u64, + pub NotesMarkdown: String, + pub NotesHtml: String, +} + +impl From for PyVelopackAsset { + fn from(value: VelopackAsset) -> Self { + PyVelopackAsset { + PackageId: value.PackageId.into(), + Version: value.Version.into(), + Type: value.Type.into(), + FileName: value.FileName.into(), + SHA1: value.SHA1.into(), + SHA256: value.SHA256.into(), + Size: value.Size, + NotesMarkdown: value.NotesMarkdown.into(), + NotesHtml: value.NotesHtml.into(), + } + } +} + +impl Into for PyVelopackAsset { + fn into(self) -> VelopackAsset { + VelopackAsset { + PackageId: self.PackageId.into(), + Version: self.Version.into(), + Type: self.Type.into(), + FileName: self.FileName.into(), + SHA1: self.SHA1.into(), + SHA256: self.SHA256.into(), + Size: self.Size, + NotesMarkdown: self.NotesMarkdown.into(), + NotesHtml: self.NotesHtml.into(), + } + } +} + +#[pyclass(name = "UpdateInfo")] +#[derive(Debug, Clone, Default)] +pub struct PyUpdateInfo { + pub TargetFullRelease: PyVelopackAsset, + pub BaseRelease: Option, + pub DeltasToTarget: Vec, + pub IsDowngrade: bool, +} + +impl From for PyUpdateInfo { + fn from(value: UpdateInfo) -> Self { + PyUpdateInfo { + TargetFullRelease: value.TargetFullRelease.into(), + BaseRelease: value.BaseRelease.map(Into::into), + DeltasToTarget: value.DeltasToTarget.into_iter().map(Into::into).collect(), + IsDowngrade: value.IsDowngrade, + } + } +} + +impl Into for PyUpdateInfo { + fn into(self) -> UpdateInfo { + UpdateInfo { + TargetFullRelease: self.TargetFullRelease.into(), + BaseRelease: self.BaseRelease.map(Into::into), + DeltasToTarget: self.DeltasToTarget.into_iter().map(Into::into).collect(), + IsDowngrade: self.IsDowngrade, + } + } +} + +#[pyclass(name = "UpdateOptions")] +#[derive(Debug, Clone, Default)] +pub struct PyUpdateOptions { + pub AllowVersionDowngrade: bool, + pub ExplicitChannel: Option, + pub MaximumDeltasBeforeFallback: i32, +} + +impl From for PyUpdateOptions { + fn from(value: UpdateOptions) -> Self { + PyUpdateOptions { + AllowVersionDowngrade: value.AllowVersionDowngrade, + ExplicitChannel: value.ExplicitChannel.map(Into::into), + MaximumDeltasBeforeFallback: value.MaximumDeltasBeforeFallback, + } + } +} + +impl Into for PyUpdateOptions { + fn into(self) -> UpdateOptions { + UpdateOptions { + AllowVersionDowngrade: self.AllowVersionDowngrade, + ExplicitChannel: self.ExplicitChannel.map(Into::into), + MaximumDeltasBeforeFallback: self.MaximumDeltasBeforeFallback, + } + } +} +