diff --git a/src/code-generator/Program.cs b/src/code-generator/Program.cs index 6e5c70c8..8c0b1245 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()! @@ -62,12 +62,8 @@ var types = new List() { TypeMap.Primitive("u64", "uint64_t"), }.ToDictionary(v => v.rustType, v => v); -var handlebarData = availableStructs.Select(s => new RustStruct_Struct { - rust_comment = s.DocComment.ToRustComment(), - cpp_comment = s.DocComment.ToCppComment(), - struct_rust_name = s.Name, - struct_c_name = types[s.Name].interopType, - fields = s.Fields.Select(f => { +var handlebarData = availableStructs.Select(s => { + var fields = s.Fields.Select(f => { var isString = types[f.Type].rustType == "PathBuf" || types[f.Type].rustType == "String"; var field = new RustStruct_Field { rust_comment = f.DocComment.ToRustComment(), @@ -83,7 +79,22 @@ var handlebarData = availableStructs.Select(s => new RustStruct_Struct { field_normal = !f.Vec && !types[f.Type].primitive, }; return field; - }).ToArray(), + }).ToArray(); + + var opt_ordered_fields = fields + .Where(f => !f.field_optional) + .Concat(fields.Where(f => f.field_optional)) + .ToArray(); + + var stru = new RustStruct_Struct { + rust_comment = s.DocComment.ToRustComment(), + cpp_comment = s.DocComment.ToCppComment(), + struct_rust_name = s.Name, + struct_c_name = types[s.Name].interopType, + fields = fields, + opt_ordered_fields = opt_ordered_fields, + }; + return stru; }).ToArray(); string rustTypes = Path.Combine(libcppDir, "src", "types.rs"); @@ -99,7 +110,7 @@ Util.ReplaceTextInFile(rustTypes, "RUST_TYPES", rustCTypes.ToString().ReplaceLin Util.ReplaceTextInFile(rustCppInclude, "CPP_TYPES", cppTypes.ToString().ReplaceLineEndings("\n")); // --- Python asset.rs generation --- -string pythonAssetRs = Path.Combine(scriptsDir, "..", "lib-python", "src", "asset.rs"); +string pythonAssetRs = Path.Combine(scriptsDir, "..", "lib-python", "src", "types.rs"); var pythonAssetTemplate = Handlebars.Compile(File.ReadAllText(Path.Combine(templatesDir, "python_asset.hbs"))); var pythonAsset = pythonAssetTemplate(handlebarData); File.WriteAllText(pythonAssetRs, pythonAsset.ToString().ReplaceLineEndings("\n")); @@ -159,6 +170,7 @@ class RustStruct_Struct public string rust_comment; public string cpp_comment; public RustStruct_Field[] fields; + public RustStruct_Field[] opt_ordered_fields; } class RustStruct_Field diff --git a/src/code-generator/Templates/python_asset.hbs b/src/code-generator/Templates/python_asset.hbs index 8044cdfa..1b8c0ccb 100644 --- a/src/code-generator/Templates/python_asset.hbs +++ b/src/code-generator/Templates/python_asset.hbs @@ -9,12 +9,42 @@ use std::path::PathBuf; #[derive(Debug, Clone, Default)] pub struct Py{{struct_rust_name}} { {{#each fields}} + #[pyo3(get, set)] 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}} } +#[pymethods] +impl Py{{struct_rust_name}} { + #[new] + #[pyo3(signature = ({{#each opt_ordered_fields}}{{field_name}}{{#if field_optional}} = None{{/if~}}{{#unless @last}}, {{/unless}}{{/each}}))] + fn new( + {{#each opt_ordered_fields}}{{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}}) -> Self { + Self { + {{#each fields}} + {{#if field_vector}} + {{field_name}}: {{field_name}}.into_iter().map(Into::into).collect(), + {{/if}} + {{#if field_primitive}} + {{field_name}}: {{field_name}}, + {{/if}} + {{#if field_optional}} + {{field_name}}: {{field_name}}.map(Into::into), + {{else}} + {{#if field_normal}} + {{field_name}}: {{field_name}}.into(), + {{/if}} + {{/if}} + {{/each}} + } + } +} + impl From<{{struct_rust_name}}> for Py{{struct_rust_name}} { fn from(value: {{struct_rust_name}}) -> Self { Py{{struct_rust_name}} { diff --git a/src/lib-python/src/types.rs b/src/lib-python/src/types.rs index 2e6dc6e7..8ac05355 100644 --- a/src/lib-python/src/types.rs +++ b/src/lib-python/src/types.rs @@ -7,14 +7,43 @@ use std::path::PathBuf; #[pyclass(name = "VelopackLocatorConfig")] #[derive(Debug, Clone, Default)] pub struct PyVelopackLocatorConfig { + #[pyo3(get, set)] pub RootAppDir: PathBuf, + #[pyo3(get, set)] pub UpdateExePath: PathBuf, + #[pyo3(get, set)] pub PackagesDir: PathBuf, + #[pyo3(get, set)] pub ManifestPath: PathBuf, + #[pyo3(get, set)] pub CurrentBinaryDir: PathBuf, + #[pyo3(get, set)] pub IsPortable: bool, } +#[pymethods] +impl PyVelopackLocatorConfig { + #[new] + #[pyo3(signature = (RootAppDir, UpdateExePath, PackagesDir, ManifestPath, CurrentBinaryDir, IsPortable))] + fn new( + RootAppDir: PathBuf, + UpdateExePath: PathBuf, + PackagesDir: PathBuf, + ManifestPath: PathBuf, + CurrentBinaryDir: PathBuf, + IsPortable: bool, + ) -> Self { + Self { + RootAppDir: RootAppDir.into(), + UpdateExePath: UpdateExePath.into(), + PackagesDir: PackagesDir.into(), + ManifestPath: ManifestPath.into(), + CurrentBinaryDir: CurrentBinaryDir.into(), + IsPortable: IsPortable, + } + } +} + impl From for PyVelopackLocatorConfig { fn from(value: VelopackLocatorConfig) -> Self { PyVelopackLocatorConfig { @@ -44,17 +73,55 @@ impl Into for PyVelopackLocatorConfig { #[pyclass(name = "VelopackAsset")] #[derive(Debug, Clone, Default)] pub struct PyVelopackAsset { + #[pyo3(get, set)] pub PackageId: String, + #[pyo3(get, set)] pub Version: String, + #[pyo3(get, set)] pub Type: String, + #[pyo3(get, set)] pub FileName: String, + #[pyo3(get, set)] pub SHA1: String, + #[pyo3(get, set)] pub SHA256: String, + #[pyo3(get, set)] pub Size: u64, + #[pyo3(get, set)] pub NotesMarkdown: String, + #[pyo3(get, set)] pub NotesHtml: String, } +#[pymethods] +impl PyVelopackAsset { + #[new] + #[pyo3(signature = (PackageId, Version, Type, FileName, SHA1, SHA256, Size, NotesMarkdown, NotesHtml))] + fn new( + PackageId: String, + Version: String, + Type: String, + FileName: String, + SHA1: String, + SHA256: String, + Size: u64, + NotesMarkdown: String, + NotesHtml: String, + ) -> Self { + Self { + PackageId: PackageId.into(), + Version: Version.into(), + Type: Type.into(), + FileName: FileName.into(), + SHA1: SHA1.into(), + SHA256: SHA256.into(), + Size: Size, + NotesMarkdown: NotesMarkdown.into(), + NotesHtml: NotesHtml.into(), + } + } +} + impl From for PyVelopackAsset { fn from(value: VelopackAsset) -> Self { PyVelopackAsset { @@ -90,12 +157,35 @@ impl Into for PyVelopackAsset { #[pyclass(name = "UpdateInfo")] #[derive(Debug, Clone, Default)] pub struct PyUpdateInfo { + #[pyo3(get, set)] pub TargetFullRelease: PyVelopackAsset, + #[pyo3(get, set)] pub BaseRelease: Option, + #[pyo3(get, set)] pub DeltasToTarget: Vec, + #[pyo3(get, set)] pub IsDowngrade: bool, } +#[pymethods] +impl PyUpdateInfo { + #[new] + #[pyo3(signature = (TargetFullRelease, DeltasToTarget, IsDowngrade, BaseRelease = None))] + fn new( + TargetFullRelease: PyVelopackAsset, + DeltasToTarget: Vec, + IsDowngrade: bool, + BaseRelease: Option, + ) -> Self { + Self { + TargetFullRelease: TargetFullRelease.into(), + BaseRelease: BaseRelease.map(Into::into), + DeltasToTarget: DeltasToTarget.into_iter().map(Into::into).collect(), + IsDowngrade: IsDowngrade, + } + } +} + impl From for PyUpdateInfo { fn from(value: UpdateInfo) -> Self { PyUpdateInfo { @@ -121,11 +211,31 @@ impl Into for PyUpdateInfo { #[pyclass(name = "UpdateOptions")] #[derive(Debug, Clone, Default)] pub struct PyUpdateOptions { + #[pyo3(get, set)] pub AllowVersionDowngrade: bool, + #[pyo3(get, set)] pub ExplicitChannel: Option, + #[pyo3(get, set)] pub MaximumDeltasBeforeFallback: i32, } +#[pymethods] +impl PyUpdateOptions { + #[new] + #[pyo3(signature = (AllowVersionDowngrade, MaximumDeltasBeforeFallback, ExplicitChannel = None))] + fn new( + AllowVersionDowngrade: bool, + MaximumDeltasBeforeFallback: i32, + ExplicitChannel: Option, + ) -> Self { + Self { + AllowVersionDowngrade: AllowVersionDowngrade, + ExplicitChannel: ExplicitChannel.map(Into::into), + MaximumDeltasBeforeFallback: MaximumDeltasBeforeFallback, + } + } +} + impl From for PyUpdateOptions { fn from(value: UpdateOptions) -> Self { PyUpdateOptions {