auto generate python dto's

This commit is contained in:
Caelan Sayler
2025-06-04 16:02:49 +01:00
committed by Caelan
parent a394ccea96
commit aff7ade8a9
4 changed files with 218 additions and 206 deletions

View File

@@ -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;
}

View File

@@ -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}}

View File

@@ -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<VelopackAsset> for PyVelopackAsset {
fn from(asset: VelopackAsset) -> Self {
PyVelopackAsset(asset)
}
}
impl From<PyVelopackAsset> for VelopackAsset {
fn from(py_asset: PyVelopackAsset) -> Self {
py_asset.0
}
}
impl AsRef<VelopackAsset> 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<PyVelopackAsset>,
deltas_to_target: Vec<PyVelopackAsset>,
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<PyVelopackAsset>,
) -> 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<PyVelopackAsset> {
self.0.BaseRelease.clone().map(PyVelopackAsset)
}
#[getter]
fn deltas_to_target(&self) -> Vec<PyVelopackAsset> {
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<UpdateInfo> for PyUpdateInfo {
fn from(info: UpdateInfo) -> Self {
PyUpdateInfo(info)
}
}
impl From<PyUpdateInfo> for UpdateInfo {
fn from(py_info: PyUpdateInfo) -> Self {
py_info.0
}
}
impl AsRef<UpdateInfo> for PyUpdateInfo {
fn as_ref(&self) -> &UpdateInfo {
&self.0
}
}

148
src/lib-python/src/types.rs Normal file
View File

@@ -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<VelopackLocatorConfig> 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<VelopackLocatorConfig> 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<VelopackAsset> 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<VelopackAsset> 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<PyVelopackAsset>,
pub DeltasToTarget: Vec<PyVelopackAsset>,
pub IsDowngrade: bool,
}
impl From<UpdateInfo> 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<UpdateInfo> 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<String>,
pub MaximumDeltasBeforeFallback: i32,
}
impl From<UpdateOptions> for PyUpdateOptions {
fn from(value: UpdateOptions) -> Self {
PyUpdateOptions {
AllowVersionDowngrade: value.AllowVersionDowngrade,
ExplicitChannel: value.ExplicitChannel.map(Into::into),
MaximumDeltasBeforeFallback: value.MaximumDeltasBeforeFallback,
}
}
}
impl Into<UpdateOptions> for PyUpdateOptions {
fn into(self) -> UpdateOptions {
UpdateOptions {
AllowVersionDowngrade: self.AllowVersionDowngrade,
ExplicitChannel: self.ExplicitChannel.map(Into::into),
MaximumDeltasBeforeFallback: self.MaximumDeltasBeforeFallback,
}
}
}