diff --git a/README.md b/README.md index ffb9602d68..4f8692804e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ The main goal of this project is to create an intuitive GUI for the most common With this app, you can easily download, install, update, and uninstall any software published on the supported package managers — and much more! ![image](https://github.com/user-attachments/assets/7cb447ca-ee8b-4bce-8561-b9332fb0139a) - +View more screenshots [here](#screenthots) Check out the [Supported Package Managers Table](#supported-package-managers) for more details! diff --git a/src/UniGetUI.Core.LanguageEngine/LanguageData.cs b/src/UniGetUI.Core.LanguageEngine/LanguageData.cs index 20a1f469a3..9b3206b4de 100644 --- a/src/UniGetUI.Core.LanguageEngine/LanguageData.cs +++ b/src/UniGetUI.Core.LanguageEngine/LanguageData.cs @@ -5,6 +5,7 @@ using UniGetUI.Core.Data; using UniGetUI.Core.Logging; using UniGetUI.PackageEngine.Enums; +using Architecture = UniGetUI.PackageEngine.Enums.Architecture; namespace UniGetUI.Core.Language { @@ -151,45 +152,17 @@ private static Person[] LoadLanguageTranslatorList() public static class CommonTranslations { - public static readonly Dictionary ArchNames = new() - { - { Architecture.X64, "x64" }, - { Architecture.X86, "x86" }, - { Architecture.Arm64, "arm64" }, - { Architecture.Arm, "arm32" }, - }; - - public static readonly Dictionary InvertedArchNames = new() - { - { "x64", Architecture.X64 }, - { "x86", Architecture.X86 }, - { "arm64", Architecture.Arm64 }, - { "arm32", Architecture.Arm }, - }; - - public static readonly Dictionary ScopeNames = new() + public static readonly Dictionary ScopeNames = new() { { PackageScope.Global, "Machine | Global" }, { PackageScope.Local, "User | Local" }, }; - public static readonly Dictionary InvertedScopeNames = new() + public static readonly Dictionary InvertedScopeNames = new() { { "Machine | Global", PackageScope.Global }, { "User | Local", PackageScope.Local }, }; - - public static readonly Dictionary ScopeNames_NonLang = new() - { - { PackageScope.Global, "machine" }, - { PackageScope.Local, "user" }, - }; - - public static readonly Dictionary InvertedScopeNames_NonLang = new() - { - { "machine", PackageScope.Global }, - { "user", PackageScope.Local }, - }; } } diff --git a/src/UniGetUI.Core.Tools/Tools.cs b/src/UniGetUI.Core.Tools/Tools.cs index 28eba4ae75..5d97385236 100644 --- a/src/UniGetUI.Core.Tools/Tools.cs +++ b/src/UniGetUI.Core.Tools/Tools.cs @@ -694,5 +694,9 @@ public static string GetCurrentLocale() { return LanguageEngine?.Locale ?? "Unset/Unknown"; } + + private static readonly HashSet illegalPathChars = Path.GetInvalidFileNameChars().ToHashSet(); + public static string MakeValidFileName(string name) + => string.Concat(name.Where(x => !illegalPathChars.Contains(x))); } } diff --git a/src/UniGetUI.PAckageEngine.Interfaces/IInstallationOptions.cs b/src/UniGetUI.PAckageEngine.Interfaces/IInstallationOptions.cs deleted file mode 100644 index 1718e6ea7e..0000000000 --- a/src/UniGetUI.PAckageEngine.Interfaces/IInstallationOptions.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Runtime.InteropServices; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Serializable; - -namespace UniGetUI.PackageEngine.Interfaces -{ - public interface IInstallationOptions - { - public bool SkipHashCheck { get; set; } - public bool InteractiveInstallation { get; set; } - public bool RunAsAdministrator { get; set; } - public string Version { get; set; } - public bool SkipMinorUpdates { get; set; } - public Architecture? Architecture { get; set; } - public PackageScope? InstallationScope { get; set; } - public List CustomParameters { get; set; } - public bool RemoveDataOnUninstall { get; set; } - public bool PreRelease { get; set; } - public string CustomInstallLocation { get; set; } - public bool OverridesNextLevelOpts { get; set; } - - /// - /// Loads and applies the options from the given SerializableInstallationOptions object to the current object. - /// - public void GetValuesFromSerializable(SerializableInstallationOptions options); - - /// - /// Returns a SerializableInstallationOptions object containing the options of the current instance. - /// - public SerializableInstallationOptions ToSerializable(); - - /// - /// Saves the current options to disk. - /// - public void SaveToDisk(); - - /// - /// Returns a string representation of the current options. - /// - public string ToString(); - } -} diff --git a/src/UniGetUI.PAckageEngine.Interfaces/IPackage.cs b/src/UniGetUI.PAckageEngine.Interfaces/IPackage.cs index 2a28c5b3ab..80527e448f 100644 --- a/src/UniGetUI.PAckageEngine.Interfaces/IPackage.cs +++ b/src/UniGetUI.PAckageEngine.Interfaces/IPackage.cs @@ -149,7 +149,7 @@ public interface IPackage : INotifyPropertyChanged, IEquatable /// False if the update is a major update or the update doesn't exist, true if it's a minor update public bool IsUpdateMinor(); - public SerializablePackage AsSerializable(); + public Task AsSerializableAsync(); public SerializableIncompatiblePackage AsSerializable_Incompatible(); } diff --git a/src/UniGetUI.PAckageEngine.Interfaces/ManagerHelpers/IPackageOperationHelper.cs b/src/UniGetUI.PAckageEngine.Interfaces/ManagerHelpers/IPackageOperationHelper.cs index c4d56649f1..9278a7aecb 100644 --- a/src/UniGetUI.PAckageEngine.Interfaces/ManagerHelpers/IPackageOperationHelper.cs +++ b/src/UniGetUI.PAckageEngine.Interfaces/ManagerHelpers/IPackageOperationHelper.cs @@ -1,4 +1,5 @@ using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.Serializable; namespace UniGetUI.PackageEngine.Interfaces.ManagerProviders { @@ -12,11 +13,9 @@ public interface IPackageOperationHelper /// that the requested operation is performed over the given package, with its corresponding /// installation options. /// - public IReadOnlyList GetParameters( - IPackage package, - IInstallationOptions options, - OperationType operation - ); + public IReadOnlyList GetParameters(IPackage package, + InstallOptions options, + OperationType operation); /// /// Returns the veredict of the given package operation, given the package, the operation type, diff --git a/src/UniGetUI.PackageEngine.Enums/Enums.cs b/src/UniGetUI.PackageEngine.Enums/Enums.cs index 5dc8dabcf0..0ae88860d3 100644 --- a/src/UniGetUI.PackageEngine.Enums/Enums.cs +++ b/src/UniGetUI.PackageEngine.Enums/Enums.cs @@ -3,12 +3,22 @@ namespace UniGetUI.PackageEngine.Enums /// /// Represents the installation scope of a package /// - public enum PackageScope + public static class PackageScope { - Global = 1, - Machine = 1, - Local = 0, - User = 0, + public static HashSet ValidValues = [Machine, User]; + public const string Machine = "machine"; + public const string Global = Machine; + public const string User = "user"; + public const string Local = User; + } + + public static class Architecture + { + public static HashSet ValidValues = [x86, x64, arm32, arm64]; + public const string x86 = "x86"; + public const string x64 = "x64"; + public const string arm32 = "arm32"; + public const string arm64 = "arm64"; } public enum DeserializedPackageStatus diff --git a/src/UniGetUI.PackageEngine.Enums/ManagerCapabilities.cs b/src/UniGetUI.PackageEngine.Enums/ManagerCapabilities.cs index 5493077b18..e9f14bf2af 100644 --- a/src/UniGetUI.PackageEngine.Enums/ManagerCapabilities.cs +++ b/src/UniGetUI.PackageEngine.Enums/ManagerCapabilities.cs @@ -28,7 +28,7 @@ public struct ManagerCapabilities public bool CanDownloadInstaller = false; public bool SupportsCustomVersions = false; public bool SupportsCustomArchitectures = false; - public Architecture[] SupportedCustomArchitectures = []; + public string[] SupportedCustomArchitectures = []; public bool SupportsCustomScopes = false; public bool SupportsPreRelease = false; public bool SupportsCustomLocations = false; diff --git a/src/UniGetUI.PackageEngine.Enums/OverridenInstallationOptions.cs b/src/UniGetUI.PackageEngine.Enums/OverridenInstallationOptions.cs index 6a1fc57e28..052363b0d0 100644 --- a/src/UniGetUI.PackageEngine.Enums/OverridenInstallationOptions.cs +++ b/src/UniGetUI.PackageEngine.Enums/OverridenInstallationOptions.cs @@ -3,12 +3,12 @@ namespace UniGetUI.PackageEngine.Structs; public struct OverridenInstallationOptions { - public PackageScope? Scope; + public string? Scope; public bool? RunAsAdministrator; public bool PowerShell_DoNotSetScopeParameter = false; public bool? WinGet_SpecifyVersion = null; - public OverridenInstallationOptions(PackageScope? scope = null, bool? runAsAdministrator = null) + public OverridenInstallationOptions(string? scope = null, bool? runAsAdministrator = null) { Scope = scope; RunAsAdministrator = runAsAdministrator; diff --git a/src/UniGetUI.PackageEngine.Managers.Cargo/Helpers/CargoPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Cargo/Helpers/CargoPkgOperationHelper.cs index 23beae6bf2..13a89b3d9d 100644 --- a/src/UniGetUI.PackageEngine.Managers.Cargo/Helpers/CargoPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Cargo/Helpers/CargoPkgOperationHelper.cs @@ -1,12 +1,14 @@ using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; namespace UniGetUI.PackageEngine.Managers.CargoManager; internal sealed class CargoPkgOperationHelper(Cargo cargo) : BasePkgOperationHelper(cargo) { - protected override IReadOnlyList _getOperationParameters(IPackage package, IInstallationOptions options, OperationType operation) + protected override IReadOnlyList _getOperationParameters(IPackage package, + InstallOptions options, OperationType operation) { var version = options.Version == string.Empty ? package.VersionString : options.Version; List parameters = operation switch diff --git a/src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs index ce581fe3f2..d81d4ece4c 100644 --- a/src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Chocolatey.cs @@ -13,6 +13,7 @@ using UniGetUI.PackageEngine.Managers.Choco; using UniGetUI.PackageEngine.Managers.PowerShellManager; using UniGetUI.PackageEngine.PackageClasses; +using Architecture = UniGetUI.PackageEngine.Enums.Architecture; namespace UniGetUI.PackageEngine.Managers.ChocolateyManager { @@ -31,7 +32,7 @@ public Chocolatey() CanRunInteractively = true, SupportsCustomVersions = true, SupportsCustomArchitectures = true, - SupportedCustomArchitectures = [Architecture.X86], + SupportedCustomArchitectures = [Architecture.x86], SupportsPreRelease = true, SupportsCustomSources = true, SupportsCustomPackageIcons = true, diff --git a/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs index 897a421f0a..6c63ec4a2f 100644 --- a/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs @@ -2,15 +2,16 @@ using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; +using Architecture = UniGetUI.PackageEngine.Enums.Architecture; namespace UniGetUI.PackageEngine.Managers.ChocolateyManager; internal sealed class ChocolateyPkgOperationHelper : BasePkgOperationHelper { public ChocolateyPkgOperationHelper(Chocolatey manager) : base(manager) { } - protected override IReadOnlyList _getOperationParameters( - IPackage package, - IInstallationOptions options, + protected override IReadOnlyList _getOperationParameters(IPackage package, + InstallOptions options, OperationType operation) { List parameters = [operation switch { @@ -31,7 +32,7 @@ protected override IReadOnlyList _getOperationParameters( { parameters.Add("--no-progress"); - if (options.Architecture == Architecture.X86) + if (options.Architecture == Architecture.x86) parameters.Add("--forcex86"); if (options.PreRelease) diff --git a/src/UniGetUI.PackageEngine.Managers.Dotnet/DotNet.cs b/src/UniGetUI.PackageEngine.Managers.Dotnet/DotNet.cs index 85cb10dce2..e1476affdf 100644 --- a/src/UniGetUI.PackageEngine.Managers.Dotnet/DotNet.cs +++ b/src/UniGetUI.PackageEngine.Managers.Dotnet/DotNet.cs @@ -12,6 +12,7 @@ using UniGetUI.PackageEngine.Managers.PowerShellManager; using UniGetUI.PackageEngine.PackageClasses; using UniGetUI.PackageEngine.Structs; +using Architecture = UniGetUI.PackageEngine.Enums.Architecture; namespace UniGetUI.PackageEngine.Managers.DotNetManager { @@ -28,7 +29,7 @@ public DotNet() CanDownloadInstaller = true, SupportsCustomScopes = true, SupportsCustomArchitectures = true, - SupportedCustomArchitectures = [Architecture.X86, Architecture.X64, Architecture.Arm64, Architecture.Arm], + SupportedCustomArchitectures = [Architecture.x86, Architecture.x64, Architecture.arm64, Architecture.arm32], SupportsPreRelease = true, SupportsCustomLocations = true, SupportsCustomPackageIcons = true, diff --git a/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs index b360d7a74f..7613cb23b7 100644 --- a/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs @@ -2,15 +2,16 @@ using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; +using Architecture = UniGetUI.PackageEngine.Enums.Architecture; namespace UniGetUI.PackageEngine.Managers.DotNetManager; internal sealed class DotNetPkgOperationHelper : BasePkgOperationHelper { public DotNetPkgOperationHelper(DotNet manager) : base(manager) { } - protected override IReadOnlyList _getOperationParameters( - IPackage package, - IInstallationOptions options, + protected override IReadOnlyList _getOperationParameters(IPackage package, + InstallOptions options, OperationType operation) { List parameters = @@ -41,10 +42,10 @@ protected override IReadOnlyList _getOperationParameters( { parameters.AddRange(options.Architecture switch { - Architecture.X86 => ["--arch", "x86"], - Architecture.X64 => ["--arch", "x64"], - Architecture.Arm => ["--arch", "arm32"], - Architecture.Arm64 => ["--arch", "arm64"], + Architecture.x86 => ["--arch", "x86"], + Architecture.x64 => ["--arch", "x64"], + Architecture.arm32 => ["--arch", "arm32"], + Architecture.arm64 => ["--arch", "arm64"], _ => [] }); } diff --git a/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs index 5d6d2d437c..c1d9be7829 100644 --- a/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs +++ b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs @@ -59,7 +59,7 @@ protected sealed override IReadOnlyList FindPackages_UnSafe(string quer sources = [ Properties.DefaultSource ]; } - bool canPrerelease = InstallationOptions.LoadForManager(this).PreRelease; + bool canPrerelease = InstallOptionsFactory.LoadForManager(this).PreRelease; foreach(IManagerSource source in sources) { @@ -154,7 +154,7 @@ protected override IReadOnlyList GetAvailableUpdates_UnSafe() if (!sourceMapping.ContainsKey(uri)) sourceMapping[uri] = new(); sourceMapping[uri].Add(package); } - bool canPrerelease = InstallationOptions.LoadForManager(this).PreRelease; + bool canPrerelease = InstallOptionsFactory.LoadForManager(this).PreRelease; foreach (var pair in sourceMapping) { diff --git a/src/UniGetUI.PackageEngine.Managers.Npm/Helpers/NpmPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Npm/Helpers/NpmPkgOperationHelper.cs index ec718e7786..834f4ae6e9 100644 --- a/src/UniGetUI.PackageEngine.Managers.Npm/Helpers/NpmPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Npm/Helpers/NpmPkgOperationHelper.cs @@ -1,13 +1,15 @@ using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; namespace UniGetUI.PackageEngine.Managers.NpmManager; internal sealed class NpmPkgOperationHelper : BasePkgOperationHelper { public NpmPkgOperationHelper(Npm manager) : base(manager) { } - protected override IReadOnlyList _getOperationParameters(IPackage package, IInstallationOptions options, OperationType operation) + protected override IReadOnlyList _getOperationParameters(IPackage package, + InstallOptions options, OperationType operation) { List parameters = operation switch { OperationType.Install => [Manager.Properties.InstallVerb, $"{package.Id}@{(options.Version == string.Empty? package.VersionString: options.Version)}"], diff --git a/src/UniGetUI.PackageEngine.Managers.Pip/Helpers/PipPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Pip/Helpers/PipPkgOperationHelper.cs index 10be39572d..9df3b861a9 100644 --- a/src/UniGetUI.PackageEngine.Managers.Pip/Helpers/PipPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Pip/Helpers/PipPkgOperationHelper.cs @@ -1,15 +1,15 @@ using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; namespace UniGetUI.PackageEngine.Managers.PipManager; internal sealed class PipPkgOperationHelper : BasePkgOperationHelper { public PipPkgOperationHelper(Pip manager) : base(manager) { } - protected override IReadOnlyList _getOperationParameters( - IPackage package, - IInstallationOptions options, + protected override IReadOnlyList _getOperationParameters(IPackage package, + InstallOptions options, OperationType operation) { List parameters = [operation switch { diff --git a/src/UniGetUI.PackageEngine.Managers.PowerShell/Helpers/PowerShellPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.PowerShell/Helpers/PowerShellPkgOperationHelper.cs index d7bfa540e6..d72bbbce46 100644 --- a/src/UniGetUI.PackageEngine.Managers.PowerShell/Helpers/PowerShellPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.PowerShell/Helpers/PowerShellPkgOperationHelper.cs @@ -1,13 +1,15 @@ using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; namespace UniGetUI.PackageEngine.Managers.PowerShellManager; internal sealed class PowerShellPkgOperationHelper : BasePkgOperationHelper { public PowerShellPkgOperationHelper(PowerShell manager) : base(manager) { } - protected override IReadOnlyList _getOperationParameters(IPackage package, IInstallationOptions options, OperationType operation) + protected override IReadOnlyList _getOperationParameters(IPackage package, + InstallOptions options, OperationType operation) { List parameters = [operation switch { OperationType.Install => Manager.Properties.InstallVerb, diff --git a/src/UniGetUI.PackageEngine.Managers.PowerShell7/Helpers/PowerShell7PkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.PowerShell7/Helpers/PowerShell7PkgOperationHelper.cs index 56bc0a32ea..b28741295d 100644 --- a/src/UniGetUI.PackageEngine.Managers.PowerShell7/Helpers/PowerShell7PkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.PowerShell7/Helpers/PowerShell7PkgOperationHelper.cs @@ -1,13 +1,15 @@ using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; namespace UniGetUI.PackageEngine.Managers.PowerShell7Manager; internal sealed class PowerShell7PkgOperationHelper : BasePkgOperationHelper { public PowerShell7PkgOperationHelper(PowerShell7 manager) : base(manager) { } - protected override IReadOnlyList _getOperationParameters(IPackage package, IInstallationOptions options, OperationType operation) + protected override IReadOnlyList _getOperationParameters(IPackage package, + InstallOptions options, OperationType operation) { List parameters = [operation switch { OperationType.Install => Manager.Properties.InstallVerb, diff --git a/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs index c3c6ae2033..211e5eb5b9 100644 --- a/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs @@ -2,13 +2,16 @@ using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; +using Architecture = UniGetUI.PackageEngine.Enums.Architecture; namespace UniGetUI.PackageEngine.Managers.ScoopManager; internal sealed class ScoopPkgOperationHelper : BasePkgOperationHelper { public ScoopPkgOperationHelper(Scoop manager) : base(manager) { } - protected override IReadOnlyList _getOperationParameters(IPackage package, IInstallationOptions options, OperationType operation) + protected override IReadOnlyList _getOperationParameters(IPackage package, + InstallOptions options, OperationType operation) { List parameters = [operation switch { OperationType.Install => Manager.Properties.InstallVerb, @@ -48,9 +51,9 @@ protected override IReadOnlyList _getOperationParameters(IPackage packag { parameters.AddRange(options.Architecture switch { - Architecture.X64 => ["--arch", "64bit"], - Architecture.X86 => ["--arch", "32bit"], - Architecture.Arm64 => ["--arch", "arm64"], + Architecture.x64 => ["--arch", "64bit"], + Architecture.x86 => ["--arch", "32bit"], + Architecture.arm64 => ["--arch", "arm64"], _ => [] }); } diff --git a/src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs b/src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs index e406d411a3..09738a717c 100644 --- a/src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs +++ b/src/UniGetUI.PackageEngine.Managers.Scoop/Scoop.cs @@ -15,6 +15,7 @@ using UniGetUI.PackageEngine.ManagerClasses.Manager; using UniGetUI.PackageEngine.PackageClasses; using UniGetUI.PackageEngine.Structs; +using Architecture = UniGetUI.PackageEngine.Enums.Architecture; namespace UniGetUI.PackageEngine.Managers.ScoopManager { @@ -52,7 +53,7 @@ public Scoop() CanDownloadInstaller = true, CanRemoveDataOnUninstall = true, SupportsCustomArchitectures = true, - SupportedCustomArchitectures = [Architecture.X86, Architecture.X64, Architecture.Arm64], + SupportedCustomArchitectures = [Architecture.x86, Architecture.x64, Architecture.arm64], SupportsCustomScopes = true, SupportsCustomSources = true, Sources = new SourceCapabilities diff --git a/src/UniGetUI.PackageEngine.Managers.Vcpkg/Helpers/VcpkgPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Vcpkg/Helpers/VcpkgPkgOperationHelper.cs index a005acc842..b29410702b 100644 --- a/src/UniGetUI.PackageEngine.Managers.Vcpkg/Helpers/VcpkgPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Vcpkg/Helpers/VcpkgPkgOperationHelper.cs @@ -1,13 +1,15 @@ using UniGetUI.PackageEngine.Classes.Manager.BaseProviders; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; namespace UniGetUI.PackageEngine.Managers.VcpkgManager; internal sealed class VcpkgPkgOperationHelper : BasePkgOperationHelper { public VcpkgPkgOperationHelper(Vcpkg manager) : base(manager) { } - protected override IReadOnlyList _getOperationParameters(IPackage package, IInstallationOptions options, OperationType operation) + protected override IReadOnlyList _getOperationParameters(IPackage package, + InstallOptions options, OperationType operation) { List parameters = operation switch { OperationType.Install => [Manager.Properties.InstallVerb, package.Id], diff --git a/src/UniGetUI.PackageEngine.Managers.Vcpkg/Vcpkg.cs b/src/UniGetUI.PackageEngine.Managers.Vcpkg/Vcpkg.cs index b8eff47830..b1262b04ad 100644 --- a/src/UniGetUI.PackageEngine.Managers.Vcpkg/Vcpkg.cs +++ b/src/UniGetUI.PackageEngine.Managers.Vcpkg/Vcpkg.cs @@ -13,6 +13,7 @@ using UniGetUI.PackageEngine.ManagerClasses.Classes; using UniGetUI.PackageEngine.ManagerClasses.Manager; using UniGetUI.PackageEngine.PackageClasses; +using Architecture = System.Runtime.InteropServices.Architecture; namespace UniGetUI.PackageEngine.Managers.VcpkgManager { @@ -459,9 +460,9 @@ public static string GetDefaultTriplet() if (DefaultTriplet == "") { - if (RuntimeInformation.OSArchitecture == Architecture.X64) DefaultTriplet = "x64-"; - else if (RuntimeInformation.OSArchitecture == Architecture.X86) DefaultTriplet = "x86-"; - else if (RuntimeInformation.OSArchitecture == Architecture.Arm64) DefaultTriplet = "arm64-"; + if (RuntimeInformation.OSArchitecture is Architecture.X64) DefaultTriplet = "x64-"; + else if (RuntimeInformation.OSArchitecture is Architecture.X86) DefaultTriplet = "x86-"; + else if (RuntimeInformation.OSArchitecture is Architecture.Arm64) DefaultTriplet = "arm64-"; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) DefaultTriplet += "windows"; if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) DefaultTriplet += "osx"; diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativePackageHandler.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativePackageHandler.cs index 8c2734c2c9..5dc9607547 100644 --- a/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativePackageHandler.cs +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativePackageHandler.cs @@ -4,6 +4,8 @@ using UniGetUI.Core.Logging; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; +using InstallOptions = UniGetUI.PackageEngine.Serializable.InstallOptions; namespace UniGetUI.PackageEngine.Managers.WingetManager; @@ -79,7 +81,7 @@ public static void AddPackage(IPackage package, CatalogPackage catalogPackage) /// /// Get (cached or load) the native installer for the given package, if any. The operation type determines wether /// - public static PackageInstallerInfo? GetInstallationOptions(IPackage package, IInstallationOptions unigetuiOptions, OperationType operation) + public static PackageInstallerInfo? GetInstallationOptions(IPackage package, InstallOptions unigetuiOptions, OperationType operation) // => TaskRecycler.RunOrAttach(_getInstallationOptions, package, operation); // //private static PackageInstallerInfo? _getInstallationOptions(IPackage package, OperationType operation) @@ -105,7 +107,7 @@ public static void Clear() } private static PackageInstallerInfo? _getInstallationOptionsOnDict(IPackage package, - ref ConcurrentDictionary source, bool installed, IInstallationOptions unigetuiOptions) + ref ConcurrentDictionary source, bool installed, InstallOptions unigetuiOptions) { if (source.TryGetValue(package.GetHash(), out PackageInstallerInfo? installerInfo)) return installerInfo; @@ -114,8 +116,8 @@ public static void Clear() if (installed) catalogPackage = GetPackage(package)?.InstalledVersion; else catalogPackage = GetPackage(package)?.DefaultInstallVersion; - InstallOptions? wingetOptions = NativeWinGetHelper.ExternalFactory?.CreateInstallOptions(); - InstallOptions? wingetDefaultOptions = NativeWinGetHelper.ExternalFactory?.CreateInstallOptions(); + Microsoft.Management.Deployment.InstallOptions? wingetOptions = NativeWinGetHelper.ExternalFactory?.CreateInstallOptions(); + Microsoft.Management.Deployment.InstallOptions? wingetDefaultOptions = NativeWinGetHelper.ExternalFactory?.CreateInstallOptions(); if (wingetOptions is null) { diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs index 9b9e98a936..7a46cba547 100644 --- a/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs @@ -7,6 +7,9 @@ using UniGetUI.PackageEngine.Classes.Packages.Classes; using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; +using Architecture = UniGetUI.PackageEngine.Enums.Architecture; +using InstallOptions = UniGetUI.PackageEngine.Serializable.InstallOptions; namespace UniGetUI.PackageEngine.Managers.WingetManager; internal sealed class WinGetPkgOperationHelper : BasePkgOperationHelper @@ -24,7 +27,8 @@ public static string GetIdNamePiece(IPackage package) public WinGetPkgOperationHelper(WinGet manager) : base(manager) { } - protected override IReadOnlyList _getOperationParameters(IPackage package, IInstallationOptions options, OperationType operation) + protected override IReadOnlyList _getOperationParameters(IPackage package, + InstallOptions options, OperationType operation) { List parameters = [operation switch { OperationType.Install => Manager.Properties.InstallVerb, @@ -60,11 +64,11 @@ protected override IReadOnlyList _getOperationParameters(IPackage packag { if (package.Name.Contains("64-bit") || package.Id.ToLower().Contains("x64")) { - options.Architecture = Architecture.X64; + options.Architecture = Architecture.x64; } else if (package.Name.Contains("32-bit") || package.Id.ToLower().Contains("x86")) { - options.Architecture = Architecture.X86; + options.Architecture = Architecture.x86; } parameters.Add("--include-unknown"); } @@ -81,9 +85,9 @@ protected override IReadOnlyList _getOperationParameters(IPackage packag parameters.AddRange(options.Architecture switch { - Architecture.X86 => ["--architecture", "x86"], - Architecture.X64 => ["--architecture", "x64"], - Architecture.Arm64 => ["--architecture", "arm64"], + Architecture.x86 => ["--architecture", "x86"], + Architecture.x64 => ["--architecture", "x64"], + Architecture.arm64 => ["--architecture", "arm64"], _ => [] }); } diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs index 42fd93641d..3f035d4a6d 100644 --- a/src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs @@ -15,6 +15,7 @@ using UniGetUI.PackageEngine.ManagerClasses.Classes; using UniGetUI.PackageEngine.ManagerClasses.Manager; using UniGetUI.PackageEngine.PackageClasses; +using Architecture = UniGetUI.PackageEngine.Enums.Architecture; namespace UniGetUI.PackageEngine.Managers.WingetManager { @@ -45,7 +46,7 @@ public WinGet() SupportsCustomVersions = true, CanDownloadInstaller = true, SupportsCustomArchitectures = true, - SupportedCustomArchitectures = [Architecture.X86, Architecture.X64, Architecture.Arm64], + SupportedCustomArchitectures = [Architecture.x86, Architecture.x64, Architecture.arm64], SupportsCustomScopes = true, SupportsCustomLocations = true, SupportsCustomSources = true, diff --git a/src/UniGetUI.PackageEngine.Operations/AbstractOperation.cs b/src/UniGetUI.PackageEngine.Operations/AbstractOperation.cs index 6083498315..340169c53a 100644 --- a/src/UniGetUI.PackageEngine.Operations/AbstractOperation.cs +++ b/src/UniGetUI.PackageEngine.Operations/AbstractOperation.cs @@ -82,9 +82,9 @@ public class BadgeCollection public readonly bool AsAdministrator; public readonly bool Interactive; public readonly bool SkipHashCheck; - public readonly PackageScope? Scope; + public readonly string? Scope; - public BadgeCollection(bool admin, bool interactive, bool skiphash, PackageScope? scope) + public BadgeCollection(bool admin, bool interactive, bool skiphash, string? scope) { AsAdministrator = admin; Interactive = interactive; @@ -92,7 +92,7 @@ public BadgeCollection(bool admin, bool interactive, bool skiphash, PackageScope Scope = scope; } } - public void ApplyCapabilities(bool admin, bool interactive, bool skiphash, PackageScope? scope) + public void ApplyCapabilities(bool admin, bool interactive, bool skiphash, string? scope) { BadgesChanged?.Invoke(this, new BadgeCollection(admin, interactive, skiphash, scope)); } diff --git a/src/UniGetUI.PackageEngine.Operations/PackageOperations.cs b/src/UniGetUI.PackageEngine.Operations/PackageOperations.cs index b2686c7d2d..80c75372f6 100644 --- a/src/UniGetUI.PackageEngine.Operations/PackageOperations.cs +++ b/src/UniGetUI.PackageEngine.Operations/PackageOperations.cs @@ -8,6 +8,7 @@ using UniGetUI.PackageEngine.Interfaces; using UniGetUI.PackageEngine.Managers.WingetManager; using UniGetUI.PackageEngine.PackageLoader; +using UniGetUI.PackageEngine.Serializable; using UniGetUI.PackageOperations; namespace UniGetUI.PackageEngine.Operations @@ -17,7 +18,7 @@ public abstract class PackageOperation : AbstractProcessOperation protected List DesktopShortcutsBeforeStart = []; public readonly IPackage Package; - public readonly IInstallationOptions Options; + public readonly InstallOptions Options; public readonly OperationType Role; protected abstract Task HandleSuccess(); @@ -26,7 +27,7 @@ public abstract class PackageOperation : AbstractProcessOperation public PackageOperation( IPackage package, - IInstallationOptions options, + InstallOptions options, OperationType role, bool IgnoreParallelInstalls = false, AbstractOperation? req = null) @@ -134,7 +135,7 @@ public class InstallPackageOperation : PackageOperation public InstallPackageOperation( IPackage package, - IInstallationOptions options, + InstallOptions options, bool IgnoreParallelInstalls = false, AbstractOperation? req = null) : base(package, options, OperationType.Install, IgnoreParallelInstalls, req) @@ -183,7 +184,7 @@ public class UpdatePackageOperation : PackageOperation public UpdatePackageOperation( IPackage package, - IInstallationOptions options, + InstallOptions options, bool IgnoreParallelInstalls = false, AbstractOperation? req = null) : base(package, options, OperationType.Update, IgnoreParallelInstalls, req) @@ -237,7 +238,7 @@ public class UninstallPackageOperation : PackageOperation public UninstallPackageOperation( IPackage package, - IInstallationOptions options, + InstallOptions options, bool IgnoreParallelInstalls = false, AbstractOperation? req = null) : base(package, options, OperationType.Uninstall, IgnoreParallelInstalls, req) diff --git a/src/UniGetUI.PackageEngine.PackageLoader/PackageBundlesLoader.cs b/src/UniGetUI.PackageEngine.PackageLoader/PackageBundlesLoader.cs index 2540ecbb1a..e2ae0771d9 100644 --- a/src/UniGetUI.PackageEngine.PackageLoader/PackageBundlesLoader.cs +++ b/src/UniGetUI.PackageEngine.PackageLoader/PackageBundlesLoader.cs @@ -40,6 +40,7 @@ protected override Task WhenAddingPackage(IPackage package) public async Task AddPackagesAsync(IReadOnlyList foreign_packages) { + List added = new(); foreach (IPackage foreign in foreign_packages) { IPackage? package = null; @@ -54,7 +55,7 @@ public async Task AddPackagesAsync(IReadOnlyList foreign_packages) else { Logger.Debug($"Adding native package with id={native.Id} to bundle as a VALID package..."); - package = new ImportedPackage(await Task.Run(native.AsSerializable), native.Manager, native.Source); + package = new ImportedPackage(await native.AsSerializableAsync(), native.Manager, native.Source); } } else if (foreign is ImportedPackage imported) @@ -71,9 +72,14 @@ public async Task AddPackagesAsync(IReadOnlyList foreign_packages) { Logger.Error($"An IPackage instance id={foreign.Id} did not match the types Package, ImportedPackage or InvalidImportedPackage. This should never be the case"); } - if (package is not null && !Contains(package)) AddPackage(package); + + if (package is not null && !Contains(package)) + { + AddPackage(package); + added.Add(package); + } } - InvokePackagesChangedEvent(true, foreign_packages, []); + InvokePackagesChangedEvent(true, added, []); } public void RemoveRange(IReadOnlyList packages) diff --git a/src/UniGetUI.PackageEngine.PackageLoader/UpgradablePackagesLoader.cs b/src/UniGetUI.PackageEngine.PackageLoader/UpgradablePackagesLoader.cs index e2f6c53634..6d524df40e 100644 --- a/src/UniGetUI.PackageEngine.PackageLoader/UpgradablePackagesLoader.cs +++ b/src/UniGetUI.PackageEngine.PackageLoader/UpgradablePackagesLoader.cs @@ -37,7 +37,7 @@ protected override async Task IsPackageValid(IPackage package) IgnoredPackages[package.Id] = package; return false; } - if ((await InstallationOptions.LoadApplicableAsync(package)).SkipMinorUpdates && package.IsUpdateMinor()) + if ((await InstallOptionsFactory.LoadApplicableAsync(package)).SkipMinorUpdates && package.IsUpdateMinor()) { Logger.Info($"Ignoring package {package.Id} because it is a minor update ({package.VersionString} -> {package.NewVersionString}) and SkipMinorUpdates is set to true."); return false; diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/NullPackageManager.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/NullPackageManager.cs index a424b0dd98..0fce81ae8d 100644 --- a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/NullPackageManager.cs +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Classes/NullPackageManager.cs @@ -9,6 +9,7 @@ using UniGetUI.PackageEngine.ManagerClasses.Classes; using UniGetUI.PackageEngine.ManagerClasses.Manager; using UniGetUI.PackageEngine.PackageClasses; +using UniGetUI.PackageEngine.Serializable; namespace UniGetUI.PackageEngine.Classes.Manager { @@ -107,7 +108,8 @@ internal sealed class NullPkgDetailsHelper : IPackageDetailsHelper internal sealed class NullPkgOperationHelper : IPackageOperationHelper { - public IReadOnlyList GetParameters(IPackage package, IInstallationOptions options, OperationType operation) + public IReadOnlyList GetParameters(IPackage package, InstallOptions options, + OperationType operation) => throw new NotImplementedException(); public OperationVeredict GetResult(IPackage package, OperationType operation, IReadOnlyList processOutput, int returnCode) diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Helpers/BasePkgOperationHelper.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Helpers/BasePkgOperationHelper.cs index 2b44f013d3..31c017b42b 100644 --- a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Helpers/BasePkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Helpers/BasePkgOperationHelper.cs @@ -2,6 +2,7 @@ using UniGetUI.PackageEngine.Enums; using UniGetUI.PackageEngine.Interfaces; using UniGetUI.PackageEngine.Interfaces.ManagerProviders; +using UniGetUI.PackageEngine.Serializable; namespace UniGetUI.PackageEngine.Classes.Manager.BaseProviders; @@ -14,9 +15,8 @@ public BasePkgOperationHelper(IPackageManager manager) Manager = manager; } - protected abstract IReadOnlyList _getOperationParameters( - IPackage package, - IInstallationOptions options, + protected abstract IReadOnlyList _getOperationParameters(IPackage package, + InstallOptions options, OperationType operation); protected abstract OperationVeredict _getOperationResult( @@ -25,27 +25,14 @@ protected abstract OperationVeredict _getOperationResult( IReadOnlyList processOutput, int returnCode); - public IReadOnlyList GetParameters( - IPackage package, - IInstallationOptions options, + public IReadOnlyList GetParameters(IPackage package, + InstallOptions options, OperationType operation) { - var parameters = _getOperationParameters(package, options, operation).ToArray(); - - for (int i = 0; i < parameters.Length; i++) - { - parameters[i] = parameters[i] - .Replace("&", "") - .Replace("|", "") - .Replace(";", "") - .Replace("<", "") - .Replace(">", "") - .Replace("\n", ""); - } - + var parameters = _getOperationParameters(package, options, operation); Logger.Info($"Loaded operation parameters for package id={package.Id} on manager {Manager.Name} and operation {operation}: " + string.Join(' ', parameters)); - return parameters; + return parameters.Where(x => x.Any()).ToArray(); } diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallOptionsFactory.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallOptionsFactory.cs new file mode 100644 index 0000000000..b0121b0f46 --- /dev/null +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallOptionsFactory.cs @@ -0,0 +1,190 @@ +using System.Collections.Concurrent; +using System.Runtime.InteropServices; +using System.Text.Json; +using System.Text.Json.Nodes; +using ABI.Windows.UI.Text.Core; +using UniGetUI.Core.Data; +using UniGetUI.Core.Language; +using UniGetUI.Core.Logging; +using UniGetUI.Core.Tools; +using UniGetUI.PackageEngine.Enums; +using UniGetUI.PackageEngine.Interfaces; +using UniGetUI.PackageEngine.Serializable; + +namespace UniGetUI.PackageEngine.PackageClasses +{ + + /// + /// This class represents the options in which a package must be installed, updated or uninstalled. + /// + public static class InstallOptionsFactory + { + private static class StoragePath + { + public static string Get(IPackageManager manager) + => "GlobalValues." + manager.Name.Replace(" ", "").Replace(".", ""); + + public static string Get(IPackage package) + => package.Manager.Name.Replace(" ", "").Replace(".", "") + "." + package.Id; + } + + + // Loading from disk (package and manager) + public static InstallOptions LoadForPackage(IPackage package) + => _loadFromDisk(StoragePath.Get(package)); + + public static Task LoadForPackageAsync(IPackage package) + => Task.Run(() => LoadForPackage(package)); + + public static InstallOptions LoadForManager(IPackageManager manager) + => _loadFromDisk(StoragePath.Get(manager)); + + public static Task LoadForManagerAsync(IPackageManager manager) + => Task.Run(() => LoadForManager(manager)); + + // Saving to disk (package and manager) + public static void SaveForPackage(InstallOptions options, IPackage package) + => _saveToDisk(options, StoragePath.Get(package)); + + public static Task SaveForPackageAsync(InstallOptions options, IPackage package) + => Task.Run(() => _saveToDisk(options, StoragePath.Get(package))); + + public static void SaveForManager(InstallOptions options, IPackageManager manager) + => _saveToDisk(options, StoragePath.Get(manager)); + + public static Task SaveForManagerAsync(InstallOptions options, IPackageManager manager) + => Task.Run(() => _saveToDisk(options, StoragePath.Get(manager))); + + /// + /// Loads the applicable InstallationOptions, and applies + /// any required transformations in case that generic options are being used + /// + /// The package whose options to load + /// Overrides the RunAsAdmin property + /// Overrides the Interactive property + /// Overrides the SkipHashCheck property + /// Overrides the RemoveDataOnUninstall property + /// In case of on-the-fly command generation, the PACKAGE + /// options can be overriden with this object + /// The applicable InstallOptions + public static InstallOptions LoadApplicable( + IPackage package, + bool? elevated = null, + bool? interactive = null, + bool? no_integrity = null, + bool? remove_data = null, + InstallOptions? overridePackageOptions = null) + { + var instance = overridePackageOptions ?? LoadForPackage(package); + if (!instance.OverridesNextLevelOpts) + { + Logger.Debug($"Package {package.Id} does not override options, will use package manager's default..."); + instance = LoadForManager(package.Manager); + + var legalizedId = CoreTools.MakeValidFileName(package.Id); + instance.CustomInstallLocation = instance.CustomInstallLocation.Replace("%PACKAGE%", legalizedId); + } + + if (elevated is not null) instance.RunAsAdministrator = (bool)elevated; + if (interactive is not null) instance.InteractiveInstallation = (bool)interactive; + if (no_integrity is not null) instance.SkipHashCheck = (bool)no_integrity; + if (remove_data is not null) instance.RemoveDataOnUninstall = (bool)remove_data; + + return instance; + } + + /// + /// Loads the applicable InstallationOptions, and applies + /// any required transformations in case that generic options are being used + /// + /// The package whose options to load + /// Overrides the RunAsAdmin property + /// Overrides the Interactive property + /// Overrides the SkipHashCheck property + /// Overrides the RemoveDataOnUninstall property + /// In case of on-the-fly command generation, the PACKAGE + /// options can be overriden with this object + /// The applicable InstallOptions + public static Task LoadApplicableAsync( + IPackage package, + bool? elevated = null, + bool? interactive = null, + bool? no_integrity = null, + bool? remove_data = null, + InstallOptions? overridePackageOptions = null) + => Task.Run(() => LoadApplicable(package, elevated, interactive, no_integrity, remove_data)); + + /* + * + * SAVE TO DISK MECHANISMS + * + */ + + private static readonly ConcurrentDictionary _optionsCache = new(); + + private static void _saveToDisk(InstallOptions options, string key) + { + try + { + var filePath = Path.Join(CoreData.UniGetUIInstallationOptionsDirectory, key); + _optionsCache[key] = options.Copy(); + + string fileContents = JsonSerializer.Serialize( + options, + SerializationHelpers.DefaultOptions + ); + File.WriteAllText(filePath, fileContents); + } + catch (Exception ex) + { + Logger.Error($"Could not save {key} options to disk"); + Logger.Error(ex); + } + } + + private static InstallOptions _loadFromDisk(string key) + { + try + { + InstallOptions serializedOptions; + if (_optionsCache.TryGetValue(key, out var cached)) + { + // If the wanted instance is already cached + return cached.Copy(); + } + else + { + var filePath = Path.Join(CoreData.UniGetUIInstallationOptionsDirectory, key); + if (!File.Exists(filePath)) + { + // If the file where it should be stored does not exist + _optionsCache[key] = new InstallOptions(); + return new InstallOptions(); + } + else + { + // If the options are not cached, and the save file exists + var rawData = File.ReadAllText(filePath); + JsonNode? jsonData = JsonNode.Parse(rawData); + ArgumentNullException.ThrowIfNull(jsonData); + serializedOptions = new InstallOptions(jsonData); + _optionsCache[key] = serializedOptions; + return serializedOptions.Copy(); + } + } + } + catch (JsonException) + { + Logger.Warn("An error occurred while parsing package " + key + ". The file will be overwritten"); + File.WriteAllText(key, "{}"); + return new(); + } + catch (Exception e) + { + Logger.Error("Loading installation options for file " + key + " have failed: "); + Logger.Error(e); + return new(); + } + } + } +} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallationOptions.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallationOptions.cs deleted file mode 100644 index eefc240669..0000000000 --- a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallationOptions.cs +++ /dev/null @@ -1,277 +0,0 @@ -using System.Collections.Concurrent; -using System.Runtime.InteropServices; -using System.Text.Json; -using System.Text.Json.Nodes; -using UniGetUI.Core.Data; -using UniGetUI.Core.Language; -using UniGetUI.Core.Logging; -using UniGetUI.PackageEngine.Enums; -using UniGetUI.PackageEngine.Interfaces; -using UniGetUI.PackageEngine.Serializable; - -namespace UniGetUI.PackageEngine.PackageClasses -{ - - /// - /// This class represents the options in which a package must be installed, updated or uninstalled. - /// - public class InstallationOptions : IInstallationOptions - { - // private static readonly ConcurrentDictionary OptionsCache = []; - - public bool SkipHashCheck { get; set; } - public bool InteractiveInstallation { get; set; } - public bool RunAsAdministrator { get; set; } - public string Version { get; set; } = ""; - public bool SkipMinorUpdates { get; set; } - public Architecture? Architecture { get; set; } - public PackageScope? InstallationScope { get; set; } - public List CustomParameters { get; set; } = []; - public bool RemoveDataOnUninstall { get; set; } - public bool PreRelease { get; set; } - public string CustomInstallLocation { get; set; } = ""; - public bool OverridesNextLevelOpts { get; set; } - - private readonly string __save_filename; - - private InstallationOptions(string filename) - { - __save_filename = Path.Join(CoreData.UniGetUIInstallationOptionsDirectory, filename); - } - - private static class StoragePath - { - public static string Get(IPackageManager manager) - => "GlobalValues." + manager.Name.Replace(" ", "").Replace(".", ""); - - public static string Get(IPackage package) - => package.Manager.Name.Replace(" ", "").Replace(".", "") + "." + package.Id; - } - - public static InstallationOptions CreateEmpty(IPackage package) - { - var pkg_path = StoragePath.Get(package); - return new(pkg_path); - } - - public static InstallationOptions CreateEmpty(IPackageManager manager) - { - var mgr_path = StoragePath.Get(manager); - return new(mgr_path); - } - - public static InstallationOptions FromSerialized(SerializableInstallationOptions options, IPackage package) - { - var instance = CreateEmpty(package); - instance.GetValuesFromSerializable(options); - return instance; - } - - public static InstallationOptions FromSerialized(SerializableInstallationOptions options, IPackageManager manager) - { - var instance = CreateEmpty(manager); - instance.GetValuesFromSerializable(options); - return instance; - } - - public static InstallationOptions LoadForPackage(IPackage package) - { - InstallationOptions instance = CreateEmpty(package); - instance.LoadFromDisk(); - return instance; - } - - public static async Task LoadForPackageAsync(IPackage package) - { - InstallationOptions instance = CreateEmpty(package); - await Task.Run(instance.LoadFromDisk); - return instance; - } - - public static InstallationOptions LoadForManager(IPackageManager manager) - { - InstallationOptions instance = CreateEmpty(manager); - instance.LoadFromDisk(); - return instance; - } - - public static InstallationOptions LoadApplicable( - IPackage package, - bool? elevated = null, - bool? interactive = null, - bool? no_integrity = null, - bool? remove_data = null) - { - InstallationOptions instance = LoadForPackage(package); - if (!instance.OverridesNextLevelOpts) - { - instance = LoadForManager(package.Manager); - Logger.Debug($"Package {package.Id} does not override options, will use package manager's default..."); - } - - if (elevated is not null) instance.RunAsAdministrator = (bool)elevated; - if (interactive is not null) instance.InteractiveInstallation = (bool)interactive; - if (no_integrity is not null) instance.SkipHashCheck = (bool)no_integrity; - if (remove_data is not null) instance.RemoveDataOnUninstall = (bool)remove_data; - - return instance; - } - - public static Task LoadApplicableAsync( - IPackage package, - bool? elevated = null, - bool? interactive = null, - bool? no_integrity = null, - bool? remove_data = null) - => Task.Run(() => LoadApplicable(package, elevated, interactive, no_integrity, remove_data)); - - /// - /// Loads and applies the options from the given SerializableInstallationOptions object to the current object. - /// - public void GetValuesFromSerializable(SerializableInstallationOptions options) - { - SkipHashCheck = options.SkipHashCheck; - InteractiveInstallation = options.InteractiveInstallation; - RunAsAdministrator = options.RunAsAdministrator; - CustomInstallLocation = options.CustomInstallLocation; - Version = options.Version; - SkipMinorUpdates = options.SkipMinorUpdates; - PreRelease = options.PreRelease; - OverridesNextLevelOpts = options.OverridesNextLevelOpts; - - Architecture = null; - if (options.Architecture.Any() && - CommonTranslations.InvertedArchNames.TryGetValue(options.Architecture, out var name)) - { - Architecture = name; - } - - InstallationScope = null; - if (options.InstallationScope.Any() && - CommonTranslations.InvertedScopeNames_NonLang.TryGetValue(options.InstallationScope, out var value)) - { - InstallationScope = value; - } - - CustomParameters = options.CustomParameters; - } - - /// - /// Returns a SerializableInstallationOptions object containing the options of the current instance. - /// - public SerializableInstallationOptions ToSerializable() - { - SerializableInstallationOptions options = new() - { - SkipHashCheck = SkipHashCheck, - InteractiveInstallation = InteractiveInstallation, - RunAsAdministrator = RunAsAdministrator, - CustomInstallLocation = CustomInstallLocation, - PreRelease = PreRelease, - Version = Version, - SkipMinorUpdates = SkipMinorUpdates, - OverridesNextLevelOpts = OverridesNextLevelOpts - }; - - if (Architecture is not null) - options.Architecture = CommonTranslations.ArchNames[Architecture.Value]; - - if (InstallationScope is not null) - options.InstallationScope = CommonTranslations.ScopeNames_NonLang[InstallationScope.Value]; - - options.CustomParameters = CustomParameters; - return options; - } - - - private static ConcurrentDictionary _optionsCache = new(); - /// - /// Saves the current options to disk. - /// - public void SaveToDisk() - { - try - { - var dir = Path.GetDirectoryName(__save_filename); - ArgumentException.ThrowIfNullOrEmpty(dir); - if (!Directory.Exists(dir)) - { - Directory.CreateDirectory(dir); - } - - var serialized = ToSerializable(); - _optionsCache[__save_filename] = serialized; - - string fileContents = JsonSerializer.Serialize( - serialized, - SerializationHelpers.DefaultOptions - ); - File.WriteAllText(__save_filename, fileContents); - } - catch (Exception ex) - { - Logger.Error($"Could not save {__save_filename} options to disk"); - Logger.Error(ex); - } - } - - /// - /// Saves the current options to disk. - /// - public Task SaveToDiskAsync() => Task.Run(SaveToDisk); - - /// - /// Loads the options from disk. - /// - private void LoadFromDisk() - { - try - { - SerializableInstallationOptions serializedOptions; - if (_optionsCache.TryGetValue(__save_filename, out var cached)) - { - serializedOptions = cached; - } - else - { - if (!File.Exists(__save_filename)) return; - - var rawData = File.ReadAllText(__save_filename); - JsonNode? jsonData = JsonNode.Parse(rawData); - ArgumentNullException.ThrowIfNull(jsonData); - serializedOptions = new SerializableInstallationOptions(jsonData); - _optionsCache[__save_filename] = serializedOptions; - } - GetValuesFromSerializable(serializedOptions); - } - catch (JsonException) - { - Logger.Warn("An error occurred while parsing package " + __save_filename + ". The file will be overwritten"); - File.WriteAllText(__save_filename, "{}"); - } - catch (Exception e) - { - Logger.Error("Loading installation options for file " + __save_filename + " have failed: "); - Logger.Error(e); - } - } - - /// - /// Returns a string representation of the current options. - /// - public override string ToString() - { - string customparams = CustomParameters.Any() ? string.Join(",", CustomParameters) : "[]"; - return $""; - } - } -} diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/ImportedPackage.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/ImportedPackage.cs index 9200aa4259..82c29622af 100644 --- a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/ImportedPackage.cs +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/ImportedPackage.cs @@ -11,7 +11,7 @@ public partial class ImportedPackage : Package /// Construct an invalid package with a given name, id, version, source and manager, and an optional scope. /// public SerializableUpdatesOptions updates_options; - public SerializableInstallationOptions installation_options; + public InstallOptions installation_options; private readonly string _version; @@ -37,31 +37,27 @@ public ImportedPackage(SerializablePackage raw_data, IPackageManager manager, IM public async Task RegisterAndGetPackageAsync() { - await Task.Run(() => - { - InstallationOptions.FromSerialized(installation_options, this).SaveToDisk(); - }); + var package = new Package(Name, Id, _version, Source, Manager); + await InstallOptionsFactory.SaveForPackageAsync(installation_options, package); if (updates_options.UpdatesIgnored) - { await AddToIgnoredUpdatesAsync(updates_options.IgnoredVersion); - } - return new Package(Name, Id, _version, Source, Manager); + return package; } - public override SerializablePackage AsSerializable() + public override Task AsSerializableAsync() { - return new SerializablePackage + return Task.FromResult(new SerializablePackage { Id = Id, Name = Name, Version = _version, Source = Source.Name, ManagerName = Manager.Name, - InstallationOptions = installation_options, - Updates = updates_options - }; + InstallationOptions = installation_options.Copy(), + Updates = updates_options.Copy() + }); } public void FirePackageVersionChangedEvent() diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/InvalidImportedPackage.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/InvalidImportedPackage.cs index d2266fbb63..004c43cfe3 100644 --- a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/InvalidImportedPackage.cs +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/InvalidImportedPackage.cs @@ -47,7 +47,7 @@ public bool IsChecked public bool IsUpgradable { get => false; } - public PackageScope Scope { get => PackageScope.Local; set { } } + public string Scope { get => PackageScope.Local; set { } } public string SourceAsString { get; } @@ -75,7 +75,7 @@ public Task AddToIgnoredUpdatesAsync(string version = "*") return Task.CompletedTask; } - public SerializablePackage AsSerializable() + public Task AsSerializableAsync() { throw new NotImplementedException(); } diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Package.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Package.cs index 56e54d2a7e..3fa21763e9 100644 --- a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Package.cs +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Package.cs @@ -301,7 +301,7 @@ public virtual bool IsUpdateMinor() (NormalizedVersion.Patch != NormalizedNewVersion.Patch || NormalizedVersion.Remainder != NormalizedNewVersion.Remainder); } - public virtual SerializablePackage AsSerializable() + public virtual async Task AsSerializableAsync() { return new SerializablePackage { @@ -310,11 +310,11 @@ public virtual SerializablePackage AsSerializable() Version = VersionString, Source = Source.Name, ManagerName = Manager.Name, - InstallationOptions = InstallationOptions.LoadForPackage(this).ToSerializable(), + InstallationOptions = await InstallOptionsFactory.LoadForPackageAsync(this), Updates = new SerializableUpdatesOptions { - IgnoredVersion = GetIgnoredUpdatesVersionAsync().GetAwaiter().GetResult(), - UpdatesIgnored = HasUpdatesIgnoredAsync().GetAwaiter().GetResult(), + IgnoredVersion = await GetIgnoredUpdatesVersionAsync(), + UpdatesIgnored = await HasUpdatesIgnoredAsync(), } }; } diff --git a/src/UniGetUI.PackageEngine.Serializable.Tests/TestSerializableInstallationOptions.cs b/src/UniGetUI.PackageEngine.Serializable.Tests/TestInstallOptions.cs similarity index 90% rename from src/UniGetUI.PackageEngine.Serializable.Tests/TestSerializableInstallationOptions.cs rename to src/UniGetUI.PackageEngine.Serializable.Tests/TestInstallOptions.cs index d20cd968a4..8edf6c2071 100644 --- a/src/UniGetUI.PackageEngine.Serializable.Tests/TestSerializableInstallationOptions.cs +++ b/src/UniGetUI.PackageEngine.Serializable.Tests/TestInstallOptions.cs @@ -2,7 +2,7 @@ namespace UniGetUI.PackageEngine.Serializable.Tests; -public class TestSerializableInstallationOptions +public class TestInstallOptions { [Theory] [InlineData(false, false, "", "", "", "", "", "", false, false, false, "")] @@ -13,7 +13,7 @@ public class TestSerializableInstallationOptions public void ToAndFromJsonNode(bool a, bool b, string c, string d, string e, string f, string g, string h, bool i, bool j, bool k, string l) { - var originalObject1 = new SerializableInstallationOptions() + var originalObject1 = new InstallOptions() { SkipHashCheck = a, Architecture = h, @@ -29,7 +29,7 @@ public void ToAndFromJsonNode(bool a, bool b, string c, string d, string e, stri Assert.Equal(a, originalObject1.DiffersFromDefault()); - var object2 = new SerializableInstallationOptions(); + var object2 = new InstallOptions(); string contents = originalObject1.AsJsonString(); Assert.NotEmpty(contents); var jsonContent = JsonNode.Parse(contents); @@ -37,7 +37,7 @@ public void ToAndFromJsonNode(bool a, bool b, string c, string d, string e, stri object2.LoadFromJson(jsonContent); AreEqual(originalObject1, object2); - var object3 = new SerializableInstallationOptions(originalObject1.AsJsonNode()); + var object3 = new InstallOptions(originalObject1.AsJsonNode()); AreEqual(originalObject1, object3); var object4 = originalObject1.Copy(); @@ -77,7 +77,7 @@ public void FromJson(string JSON, bool hash, bool inter, string installLoc, stri Assert.NotEmpty(JSON); var jsonContent = JsonNode.Parse(JSON); Assert.NotNull(jsonContent); - var o2 = new SerializableInstallationOptions(jsonContent); + var o2 = new InstallOptions(jsonContent); var list = new List() { arg1, arg2, arg3 }.Where(x => x.Any()); @@ -95,7 +95,7 @@ public void FromJson(string JSON, bool hash, bool inter, string installLoc, stri Assert.Equal(ver, o2.Version); } - internal static void AreEqual(SerializableInstallationOptions o1, SerializableInstallationOptions o2) + internal static void AreEqual(InstallOptions o1, InstallOptions o2) { Assert.Equal(o1.SkipHashCheck, o2.SkipHashCheck); Assert.Equal(o1.Architecture, o2.Architecture); diff --git a/src/UniGetUI.PackageEngine.Serializable.Tests/TestSerializablePackage.cs b/src/UniGetUI.PackageEngine.Serializable.Tests/TestSerializablePackage.cs index 8d120e15f1..8a40e62443 100644 --- a/src/UniGetUI.PackageEngine.Serializable.Tests/TestSerializablePackage.cs +++ b/src/UniGetUI.PackageEngine.Serializable.Tests/TestSerializablePackage.cs @@ -5,7 +5,7 @@ namespace UniGetUI.PackageEngine.Serializable.Tests; public class TestSerializablePackage { - public static SerializableInstallationOptions TestOptions = new() + public static InstallOptions TestOptions = new() { SkipHashCheck = true, CustomParameters = ["a", "b", "c"], @@ -88,7 +88,7 @@ public void FromJson(string JSON, string id, string name, string version, string Assert.Equal(manager, o2.ManagerName); Assert.Equal(source, o2.Source); Assert.Equal(version, o2.Version); - TestSerializableInstallationOptions.AreEqual(new() { SkipHashCheck = skipHash }, o2.InstallationOptions); + TestInstallOptions.AreEqual(new() { SkipHashCheck = skipHash }, o2.InstallationOptions); TestSerializableUpdatesOptions.AreEqual(new(){IgnoredVersion = ignoredVer}, o2.Updates); } @@ -99,7 +99,7 @@ internal static void AreEqual(SerializablePackage o1, SerializablePackage o2) Assert.Equal(o1.Source, o2.Source); Assert.Equal(o1.Version, o2.Version); Assert.Equal(o1.ManagerName, o2.ManagerName); - TestSerializableInstallationOptions.AreEqual(o1.InstallationOptions, o2.InstallationOptions); + TestInstallOptions.AreEqual(o1.InstallationOptions, o2.InstallationOptions); TestSerializableUpdatesOptions.AreEqual(o1.Updates, o2.Updates); } } diff --git a/src/UniGetUI.PackageEngine.Serializable/SerializableInstallationOptions.cs b/src/UniGetUI.PackageEngine.Serializable/InstallOptions.cs similarity index 64% rename from src/UniGetUI.PackageEngine.Serializable/SerializableInstallationOptions.cs rename to src/UniGetUI.PackageEngine.Serializable/InstallOptions.cs index 7a78272847..0073e05a5a 100644 --- a/src/UniGetUI.PackageEngine.Serializable/SerializableInstallationOptions.cs +++ b/src/UniGetUI.PackageEngine.Serializable/InstallOptions.cs @@ -3,7 +3,7 @@ namespace UniGetUI.PackageEngine.Serializable { - public class SerializableInstallationOptions: SerializableComponent + public class InstallOptions: SerializableComponent { public bool SkipHashCheck { get; set; } public bool InteractiveInstallation { get; set; } @@ -16,8 +16,9 @@ public class SerializableInstallationOptions: SerializableComponent() ?? false; this.InteractiveInstallation = data[nameof(InteractiveInstallation)]?.GetVal() ?? false; this.RunAsAdministrator = data[nameof(RunAsAdministrator)]?.GetVal() ?? false; @@ -44,8 +48,9 @@ public override void LoadFromJson(JsonNode data) this.InstallationScope = data[nameof(InstallationScope)]?.GetVal() ?? ""; this.CustomParameters = new List(); - foreach(var element in data[nameof(CustomParameters)]?.AsArray2() ?? []) - if (element is not null) this.CustomParameters.Add(element.GetVal()); + foreach (var element in data[nameof(CustomParameters)]?.AsArray2() ?? []) + if (element is not null) + this.CustomParameters.Add(element.GetVal()); this.PreRelease = data[nameof(PreRelease)]?.GetVal() ?? false; this.CustomInstallLocation = data[nameof(CustomInstallLocation)]?.GetVal() ?? ""; @@ -57,6 +62,22 @@ public override void LoadFromJson(JsonNode data) // This entry shall be checked the last one, to ensure all other properties are set this.OverridesNextLevelOpts = data[nameof(OverridesNextLevelOpts)]?.GetValue() ?? DiffersFromDefault(); + + SanitizeOptions(); + } + + private void SanitizeOptions() + { + for (int i = 0; i < this.CustomParameters.Count; i++) + { + this.CustomParameters[i] = this.CustomParameters[i] + .Replace("&", "") + .Replace("|", "") + .Replace(";", "") + .Replace("<", "") + .Replace(">", "") + .Replace("\n", ""); + } } public bool DiffersFromDefault() @@ -70,17 +91,33 @@ SkipMinorUpdates is not false || InstallationScope.Any() || CustomParameters.Where(x => x != "").Any() || CustomInstallLocation.Any() || + RemoveDataOnUninstall is not false || Version.Any(); // OverridesNextLevelOpts does not need to be checked here, since // this method is invoked before this property has been set } - public SerializableInstallationOptions() : base() + public InstallOptions() : base() + { + } + + public InstallOptions(JsonNode data) : base(data) { } - public SerializableInstallationOptions(JsonNode data) : base(data) + public override string ToString() { + string customparams = CustomParameters.Any() ? string.Join(",", CustomParameters) : "[]"; + return $""; } } } diff --git a/src/UniGetUI.PackageEngine.Serializable/SerializablePackage.cs b/src/UniGetUI.PackageEngine.Serializable/SerializablePackage.cs index 12ce2117c2..46a1d0f268 100644 --- a/src/UniGetUI.PackageEngine.Serializable/SerializablePackage.cs +++ b/src/UniGetUI.PackageEngine.Serializable/SerializablePackage.cs @@ -12,7 +12,7 @@ public class SerializablePackage: SerializableComponent public string Source { get; set; } = ""; public string ManagerName { get; set; } = ""; - public SerializableInstallationOptions InstallationOptions { get; set; } = new(); + public InstallOptions InstallationOptions { get; set; } = new(); public SerializableUpdatesOptions Updates { get; set; } = new(); public override SerializablePackage Copy() diff --git a/src/UniGetUI/AppOperationHelper.cs b/src/UniGetUI/AppOperationHelper.cs index 771072ec27..9346dca993 100644 --- a/src/UniGetUI/AppOperationHelper.cs +++ b/src/UniGetUI/AppOperationHelper.cs @@ -124,13 +124,13 @@ public static void Remove(AbstractOperation op) { if (package is null) return null; - var options = await InstallationOptions.LoadApplicableAsync(package, elevated, interactive, no_integrity); - var op = new InstallPackageOperation(package, options, ignoreParallel, req); - op.OperationSucceeded += (_, _) => TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.SUCCESS, referral); - op.OperationFailed += (_, _) => TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.FAILED, referral); - Add(op); + var options = await InstallOptionsFactory.LoadApplicableAsync(package, elevated, interactive, no_integrity); + var operation = new InstallPackageOperation(package, options, ignoreParallel, req); + operation.OperationSucceeded += (_, _) => TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.SUCCESS, referral); + operation.OperationFailed += (_, _) => TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.FAILED, referral); + Add(operation); Instance.MainWindow.UpdateSystemTrayStatus(); - return op; + return operation; } public static void Install(IReadOnlyList packages, TEL_InstallReferral referral, bool? elevated = null, bool? interactive = null, bool? no_integrity = null) @@ -148,13 +148,13 @@ public static void Install(IReadOnlyList packages, TEL_InstallReferral { if (package is null) return null; - var options = await InstallationOptions.LoadApplicableAsync(package, elevated, interactive, no_integrity); - var op = new UpdatePackageOperation(package, options, ignoreParallel, req); - op.OperationSucceeded += (_, _) => TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.SUCCESS); - op.OperationFailed += (_, _) => TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.FAILED); - Add(op); + var options = await InstallOptionsFactory.LoadApplicableAsync(package, elevated, interactive, no_integrity); + var operation = new UpdatePackageOperation(package, options, ignoreParallel, req); + operation.OperationSucceeded += (_, _) => TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.SUCCESS); + operation.OperationFailed += (_, _) => TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.FAILED); + Add(operation); Instance.MainWindow.UpdateSystemTrayStatus(); - return op; + return operation; } public static void Update(IReadOnlyList packages, bool? elevated = null, bool? interactive = null, bool? no_integrity = null) @@ -221,13 +221,13 @@ public static async void ConfirmAndUninstall(IPackage? package, bool? elevated = { if (package is null) return null; - var options = await InstallationOptions.LoadApplicableAsync(package, elevated, interactive, remove_data: remove_data); - var op = new UninstallPackageOperation(package, options, ignoreParallel, req); - op.OperationSucceeded += (_, _) => TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.SUCCESS); - op.OperationFailed += (_, _) => TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.FAILED); - Add(op); + var options = await InstallOptionsFactory.LoadApplicableAsync(package, elevated, interactive, remove_data: remove_data); + var operation = new UninstallPackageOperation(package, options, ignoreParallel, req); + operation.OperationSucceeded += (_, _) => TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.SUCCESS); + operation.OperationFailed += (_, _) => TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.FAILED); + Add(operation); Instance.MainWindow.UpdateSystemTrayStatus(); - return op; + return operation; } public static void Uninstall(IReadOnlyList packages, bool? elevated = null, bool? interactive = null, bool? remove_data = null) diff --git a/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs b/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs index a7cad147c0..4797e6b942 100644 --- a/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs +++ b/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs @@ -20,17 +20,20 @@ public static partial class DialogHelper /// public static async Task ShowInstallatOptions_Continue(IPackage package, OperationType operation) { - var options = (await InstallationOptions.LoadForPackageAsync(package)).ToSerializable(); + var options = await InstallOptionsFactory.LoadForPackageAsync(package); var (dialogOptions, dialogResult) = await ShowInstallOptions(package, operation, options); - if (dialogResult != ContentDialogResult.None) + if (dialogResult is not ContentDialogResult.None) + { + Logger.Debug($"Saving updated options for package {package.Id}"); + await InstallOptionsFactory.SaveForPackageAsync(dialogOptions, package); + } + else { - InstallationOptions newOptions = await InstallationOptions.LoadForPackageAsync(package); - newOptions.GetValuesFromSerializable(dialogOptions); - await newOptions.SaveToDiskAsync(); + Logger.Debug($"Install options dialog for {package.Id} was canceled, no changes will be saved"); } - return dialogResult == ContentDialogResult.Secondary; + return dialogResult is ContentDialogResult.Secondary; } /// @@ -50,10 +53,10 @@ public static async Task ShowInstallOptions_ImportedPackage return dialogResult; } - private static async Task<(SerializableInstallationOptions, ContentDialogResult)> ShowInstallOptions( + private static async Task<(InstallOptions, ContentDialogResult)> ShowInstallOptions( IPackage package, OperationType operation, - SerializableInstallationOptions options) + InstallOptions options) { InstallOptionsPage OptionsPage = new(package, operation, options); diff --git a/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml.cs b/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml.cs index 8f6ac1e929..fd538241ab 100644 --- a/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml.cs +++ b/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml.cs @@ -9,8 +9,7 @@ using UniGetUI.PackageEngine.Interfaces; using UniGetUI.PackageEngine.PackageClasses; using UniGetUI.PackageEngine.Serializable; -using Windows.ApplicationModel; -using Windows.Gaming.XboxLive.Storage; +using Architecture = UniGetUI.PackageEngine.Enums.Architecture; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -47,7 +46,7 @@ public InstallOptions_Manager(IPackageManager manager) private async Task LoadOptions() { LoadingIndicator.Visibility = Visibility.Visible; - var options = await Task.Run(() => InstallationOptions.LoadForManager(Manager).ToSerializable()); + var options = await InstallOptionsFactory.LoadForManagerAsync(Manager); // This delay allows the spinner to show, // and give the user the sensation that things have worked @@ -85,12 +84,12 @@ private async Task LoadOptions() if (Manager.Capabilities.SupportsCustomArchitectures) { ArchitectureCombo.IsEnabled = true; - foreach (Architecture arch in Manager.Capabilities.SupportedCustomArchitectures) + foreach (string arch in Manager.Capabilities.SupportedCustomArchitectures) { - ArchitectureCombo.Items.Add(CommonTranslations.ArchNames[arch]); - if (options.Architecture == CommonTranslations.ArchNames[arch]) + ArchitectureCombo.Items.Add(arch); + if (options.Architecture == arch) { - ArchitectureCombo.SelectedValue = CommonTranslations.ArchNames[arch]; + ArchitectureCombo.SelectedValue = arch; } } } @@ -103,13 +102,13 @@ private async Task LoadOptions() { ScopeCombo.IsEnabled = true; ScopeCombo.Items.Add(CoreTools.Translate(CommonTranslations.ScopeNames[PackageScope.Local])); - if (options.InstallationScope == CommonTranslations.ScopeNames_NonLang[PackageScope.Local]) + if (options.InstallationScope == PackageScope.Local) { ScopeCombo.SelectedValue = CommonTranslations.ScopeNames[PackageScope.Local]; } ScopeCombo.Items.Add(CoreTools.Translate(CommonTranslations.ScopeNames[PackageScope.Global])); - if (options.InstallationScope == CommonTranslations.ScopeNames_NonLang[PackageScope.Global]) + if (options.InstallationScope == PackageScope.Global) { ScopeCombo.SelectedValue = CommonTranslations.ScopeNames[PackageScope.Global]; } @@ -150,7 +149,7 @@ private async Task SaveOptions() LoadingIndicator.Visibility = Visibility.Visible; DisableAllInput(); - SerializableInstallationOptions options = new(); + InstallOptions options = new(); // Checkboxes options.RunAsAdministrator = AdminCheckBox.IsChecked ?? false; options.SkipHashCheck = HashCheckBox.IsChecked ?? false; @@ -159,18 +158,18 @@ private async Task SaveOptions() // Administrator options.Architecture = ""; - string candidateValue = ArchitectureCombo.SelectedValue.ToString() ?? ""; - if (CommonTranslations.InvertedArchNames.ContainsKey(candidateValue)) + string userSelection = ArchitectureCombo.SelectedValue?.ToString() ?? ""; + if (Architecture.ValidValues.Contains(userSelection)) { - options.Architecture = candidateValue; + options.Architecture = userSelection; } // Scope options.InstallationScope = ""; - candidateValue = ScopeCombo.SelectedValue.ToString() ?? ""; - if (CommonTranslations.InvertedScopeNames.TryGetValue(candidateValue, out PackageScope result)) + userSelection = ScopeCombo.SelectedValue?.ToString() ?? ""; + if (CommonTranslations.InvertedScopeNames.TryGetValue(userSelection, out string? value)) { - options.InstallationScope = CommonTranslations.ScopeNames_NonLang[result]; + options.InstallationScope = value; } // Location @@ -183,8 +182,7 @@ private async Task SaveOptions() // Command-line parameters options.CustomParameters = CustomParameters.Text.Split(' ').Where(x => x.Any()).ToList(); - var temp = InstallationOptions.FromSerialized(options, Manager); - await temp.SaveToDiskAsync(); + await InstallOptionsFactory.SaveForManagerAsync(options, Manager); await LoadOptions(); } @@ -193,8 +191,7 @@ private async Task ResetOptions() LoadingIndicator.Visibility = Visibility.Visible; DisableAllInput(); - var opts = InstallationOptions.CreateEmpty(Manager); - await opts.SaveToDiskAsync(); + await InstallOptionsFactory.SaveForManagerAsync(new(), Manager); await LoadOptions(); } @@ -231,7 +228,7 @@ private void SelectDir_Click(object sender, RoutedEventArgs e) string folder = openPicker.Show(); if (folder != string.Empty) { - CustomInstallLocation.Text = folder; + CustomInstallLocation.Text = folder.TrimEnd('\\') + "\\%PACKAGE%"; ResetDir.IsEnabled = true; ApplyButton.Style = (Style)Application.Current.Resources["AccentButtonStyle"]; } diff --git a/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml b/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml index 6ad6c0e867..9e474e5092 100644 --- a/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml +++ b/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml @@ -61,8 +61,7 @@ + HorizontalAlignment="Stretch" /> diff --git a/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml.cs b/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml.cs index e5194ae49f..dcb35a8d6d 100644 --- a/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml.cs +++ b/src/UniGetUI/Pages/DialogPages/InstallOptions_Package.xaml.cs @@ -10,6 +10,7 @@ using UniGetUI.PackageEngine.Interfaces; using UniGetUI.PackageEngine.PackageClasses; using UniGetUI.PackageEngine.Serializable; +using Architecture = UniGetUI.PackageEngine.Enums.Architecture; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -21,15 +22,15 @@ namespace UniGetUI.Interface.Dialogs /// public sealed partial class InstallOptionsPage : Page { - public SerializableInstallationOptions Options; + public InstallOptions Options; public IPackage Package; public event EventHandler? Close; private readonly OperationType Operation; private readonly string packageInstallLocation; private bool _uiLoaded; - public InstallOptionsPage(IPackage package, SerializableInstallationOptions options) : this(package, OperationType.None, options) { } - public InstallOptionsPage(IPackage package, OperationType operation, SerializableInstallationOptions options) + public InstallOptionsPage(IPackage package, InstallOptions options) : this(package, OperationType.None, options) { } + public InstallOptionsPage(IPackage package, OperationType operation, InstallOptions options) { Package = package; InitializeComponent(); @@ -89,12 +90,12 @@ async Task LoadImage() if (Package.Manager.Capabilities.SupportsCustomArchitectures) { - foreach (Architecture arch in Package.Manager.Capabilities.SupportedCustomArchitectures) + foreach (string arch in Package.Manager.Capabilities.SupportedCustomArchitectures) { - ArchitectureComboBox.Items.Add(CommonTranslations.ArchNames[arch]); - if (Options.Architecture == CommonTranslations.ArchNames[arch]) + ArchitectureComboBox.Items.Add(arch); + if (Options.Architecture == arch) { - ArchitectureComboBox.SelectedValue = CommonTranslations.ArchNames[arch]; + ArchitectureComboBox.SelectedValue = arch; } } } @@ -137,13 +138,13 @@ async Task LoadImage() if (package.Manager.Capabilities.SupportsCustomScopes) { ScopeCombo.Items.Add(CoreTools.Translate(CommonTranslations.ScopeNames[PackageScope.Local])); - if (Options.InstallationScope == CommonTranslations.ScopeNames_NonLang[PackageScope.Local]) + if (Options.InstallationScope == PackageScope.Local) { ScopeCombo.SelectedValue = CommonTranslations.ScopeNames[PackageScope.Local]; } ScopeCombo.Items.Add(CoreTools.Translate(CommonTranslations.ScopeNames[PackageScope.Global])); - if (Options.InstallationScope == CommonTranslations.ScopeNames_NonLang[PackageScope.Global]) + if (Options.InstallationScope == PackageScope.Global) { ScopeCombo.SelectedValue = CommonTranslations.ScopeNames[PackageScope.Global]; } @@ -225,29 +226,25 @@ private async Task LoadVersions() VersionProgress.Visibility = Visibility.Collapsed; } - public async Task GetUpdatedOptions(bool updateIgnoredUpdates = true) + public async Task GetUpdatedOptions(bool updateIgnoredUpdates = true) { Options.RunAsAdministrator = AdminCheckBox?.IsChecked ?? false; Options.InteractiveInstallation = InteractiveCheckBox?.IsChecked ?? false; Options.SkipHashCheck = HashCheckbox?.IsChecked ?? false; Options.OverridesNextLevelOpts = !FollowGlobalOptionsSwitch.IsOn; - if (CommonTranslations.InvertedArchNames.ContainsKey(ArchitectureComboBox.SelectedValue.ToString() ?? "")) + Options.Architecture = ""; + var userSelection = ArchitectureComboBox.SelectedValue?.ToString() ?? ""; + if (Architecture.ValidValues.Contains(userSelection)) { - Options.Architecture = ArchitectureComboBox.SelectedValue.ToString() ?? ""; - } - else - { - Options.Architecture = ""; + Options.Architecture = userSelection; } - if (CommonTranslations.InvertedScopeNames.ContainsKey(ScopeCombo.SelectedValue.ToString() ?? "")) + Options.InstallationScope = ""; + userSelection = ScopeCombo.SelectedValue?.ToString() ?? ""; + if (CommonTranslations.InvertedScopeNames.TryGetValue(userSelection, out string? value)) { - Options.InstallationScope = CommonTranslations.ScopeNames_NonLang[CommonTranslations.InvertedScopeNames[ScopeCombo.SelectedValue.ToString() ?? ""]]; - } - else - { - Options.InstallationScope = ""; + Options.InstallationScope = value; } if (CustomInstallLocation.Text == packageInstallLocation) Options.CustomInstallLocation = ""; @@ -317,14 +314,19 @@ private void CloseButton_Click(object sender, RoutedEventArgs e) private async void GenerateCommand() { if (!_uiLoaded) return; - InstallationOptions io = InstallationOptions.FromSerialized(await GetUpdatedOptions(updateIgnoredUpdates: false), Package); + InstallOptions options = await GetUpdatedOptions(updateIgnoredUpdates: false); + if (!options.OverridesNextLevelOpts) + { + options = await InstallOptionsFactory.LoadApplicableAsync(this.Package, overridePackageOptions: options); + } + var op = ProfileComboBox.SelectedIndex switch { 1 => OperationType.Update, 2 => OperationType.Uninstall, _ => OperationType.Install, }; - var commandline = await Task.Run(() => Package.Manager.OperationHelper.GetParameters(Package, io, op)); + var commandline = await Task.Run(() => Package.Manager.OperationHelper.GetParameters(Package, options, op)); CommandBox.Text = Package.Manager.Properties.ExecutableFriendlyName + " " + string.Join(" ", commandline); } @@ -332,10 +334,5 @@ private void LayoutGrid_SizeChanged(object sender, SizeChangedEventArgs e) { if(LayoutGrid.ActualSize.Y > 1 && LayoutGrid.ActualSize.Y < double.PositiveInfinity) MaxHeight = LayoutGrid.ActualSize.Y; } - - private void ProfileComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - - } } } diff --git a/src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml.cs b/src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml.cs index 3c25199e3c..7d41ab634c 100644 --- a/src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml.cs +++ b/src/UniGetUI/Pages/DialogPages/PackageDetailsPage.xaml.cs @@ -102,7 +102,7 @@ public PackageDetailsPage(IPackage package, OperationType role, TEL_InstallRefer UpgradablePackage = Package.GetUpgradablePackage(); InstalledPackage = UpgradablePackage?.GetInstalledPackage() ?? Package.GetInstalledPackage(); - var options = InstallationOptions.LoadApplicable(package).ToSerializable(); + var options = InstallOptionsFactory.LoadForPackage(package); InstallOptionsPage = new InstallOptionsPage(package, OperationRole, options); InstallOptionsExpander.Content = InstallOptionsPage; @@ -578,12 +578,8 @@ public async void DoAction( { Close?.Invoke(this, EventArgs.Empty); - var newOptions = await Task.Run( - () => InstallationOptions.FromSerialized( - InstallOptionsPage.GetUpdatedOptions().GetAwaiter().GetResult(), - package) - ); - newOptions.SaveToDisk(); + var newOptions = await InstallOptionsPage.GetUpdatedOptions(); + await InstallOptionsFactory.SaveForPackageAsync(newOptions, package); if (AsAdmin is not null) newOptions.RunAsAdministrator = (bool)AsAdmin; if (Interactive is not null) newOptions.InteractiveInstallation = (bool)Interactive; diff --git a/src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs b/src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs index c6d2d5fb29..c97937f8c5 100644 --- a/src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs +++ b/src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs @@ -595,7 +595,7 @@ static int Comparison(IPackage x, IPackage y) foreach (IPackage package in packages) { if (package is Package && !package.Source.IsVirtualManager) - exportable.packages.Add(await Task.Run(package.AsSerializable)); + exportable.packages.Add(await package.AsSerializableAsync()); else exportable.incompatible_packages.Add(package.AsSerializable_Incompatible()); }