Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/UniGetUI.Core.Tools/SerializationHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Globalization;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization.Metadata;
Expand Down Expand Up @@ -59,7 +60,7 @@
var value = _convertXmlNode(childElement);
if (!children.ContainsKey(childElement.Name))
children[childElement.Name] = new List<object>();
children[childElement.Name].Add(value);

Check warning on line 63 in src/UniGetUI.Core.Tools/SerializationHelpers.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Possible null reference argument for parameter 'item' in 'void List<object>.Add(object item)'.

Check warning on line 63 in src/UniGetUI.Core.Tools/SerializationHelpers.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Possible null reference argument for parameter 'item' in 'void List<object>.Add(object item)'.
}
}

Expand Down
203 changes: 122 additions & 81 deletions src/UniGetUI.PackageEngine.Serializable/InstallOptions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,48 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using UniGetUI.Core.Data;

namespace UniGetUI.PackageEngine.Serializable
{
public class InstallOptions: SerializableComponent<InstallOptions>
{
private readonly IReadOnlyDictionary<string, bool> DefaultBoolValues = new Dictionary<string, bool>()
{ // OverridesNextLevelOpts is deliberately skipped here
{ "SkipHashCheck", false },
{ "InteractiveInstallation", false },
{ "RunAsAdministrator", false },
{ "PreRelease", false },
{ "SkipMinorUpdates", false },
{ "RemoveDataOnUninstall", false },
{ "UninstallPreviousVersionsOnUpdate", false },
{ "AbortOnPreInstallFail", true },
{ "AbortOnPreUpdateFail", true },
{ "AbortOnPreUninstallFail", true },
};

private readonly IReadOnlyDictionary<string, string> DefaultStringValues = new Dictionary<string, string>()
{
{"Architecture", ""},
{"InstallationScope", ""},
{"CustomInstallLocation", ""},
{"Version", ""},
{"PreInstallCommand", ""},
{"PostInstallCommand", ""},
{"PreUpdateCommand", ""},
{"PostUpdateCommand", ""},
{"PreUninstallCommand", ""},
{"PostUninstallCommand", ""},
};

private readonly IReadOnlyList<string> DefaultListValues = new List<string>()
{
"CustomParameters_Install",
"CustomParameters_Update",
"CustomParameters_Uninstall",
"KillBeforeOperation",
};


public bool SkipHashCheck { get; set; }
public bool InteractiveInstallation { get; set; }
public bool RunAsAdministrator { get; set; }
Expand All @@ -17,7 +55,6 @@ public class InstallOptions: SerializableComponent<InstallOptions>
public string CustomInstallLocation { get; set; } = "";
public string Version { get; set; } = "";
public bool SkipMinorUpdates { get; set; }
public bool OverridesNextLevelOpts { get; set; }
public bool RemoveDataOnUninstall { get; set; }
public bool UninstallPreviousVersionsOnUpdate { get; set; }
public List<string> KillBeforeOperation { get; set; } = [];
Expand All @@ -32,53 +69,54 @@ public class InstallOptions: SerializableComponent<InstallOptions>
public string PostUninstallCommand { get; set; } = "";
public bool AbortOnPreUninstallFail { get; set; } = true;

public bool OverridesNextLevelOpts { get; set; }

public override InstallOptions Copy()
{
return new()
{
SkipHashCheck = SkipHashCheck,
Architecture = Architecture,
CustomInstallLocation = CustomInstallLocation,
CustomParameters_Install = CustomParameters_Install.ToList(),
CustomParameters_Update = CustomParameters_Update.ToList(),
CustomParameters_Uninstall = CustomParameters_Uninstall.ToList(),
InstallationScope = InstallationScope,
InteractiveInstallation = InteractiveInstallation,
PreRelease = PreRelease,
RunAsAdministrator = RunAsAdministrator,
Version = Version,
SkipMinorUpdates = SkipMinorUpdates,
OverridesNextLevelOpts = OverridesNextLevelOpts,
RemoveDataOnUninstall = RemoveDataOnUninstall,
KillBeforeOperation = KillBeforeOperation.ToList(),
PreInstallCommand = PreInstallCommand,
PreUpdateCommand = PreUpdateCommand,
PreUninstallCommand = PreUninstallCommand,
PostInstallCommand = PostInstallCommand,
PostUpdateCommand = PostUpdateCommand,
PostUninstallCommand = PostUninstallCommand,
AbortOnPreInstallFail = AbortOnPreInstallFail,
AbortOnPreUpdateFail = AbortOnPreUpdateFail,
AbortOnPreUninstallFail = AbortOnPreUninstallFail,
UninstallPreviousVersionsOnUpdate = UninstallPreviousVersionsOnUpdate,
};
var copy = new InstallOptions();

foreach (var (boolKey, _) in DefaultBoolValues)
copy.SetValueToProperty(boolKey, GetValueFromProperty<bool>(boolKey));

foreach (var (stringKey, _) in DefaultStringValues)
copy.SetValueToProperty(stringKey, GetValueFromProperty<string>(stringKey));

foreach (var listKey in DefaultListValues)
copy.SetValueToProperty(listKey, GetValueFromProperty<IReadOnlyList<string>>(listKey).ToList());
Comment thread
marticliment marked this conversation as resolved.

// Handle non-automated OverridesNextLevelOpts
copy.OverridesNextLevelOpts = OverridesNextLevelOpts;
return copy;
}

public void SetValueToProperty<T>(string name, T value)
{
var property = this.GetType().GetProperty(name);
property?.SetValue(this, value);
}

public T GetValueFromProperty<T>(string name)
{
var property = this.GetType().GetProperty(name);
return (T)(property?.GetValue(this) ?? throw new InvalidDataException($"Invalid datatype for property {name} (expected {nameof(T)})"));
}

Comment thread
marticliment marked this conversation as resolved.
public override void LoadFromJson(JsonNode data)
{
// RemoveDataOnUninstall should not be loaded from disk
foreach (var (boolKey, defValue) in DefaultBoolValues)
{
// RemoveDataOnUninstall should not be loaded from disk
if(boolKey == "RemoveDataOnUninstall") continue;
SetValueToProperty(boolKey, data[boolKey]?.GetVal<bool>() ?? defValue);
}

this.SkipHashCheck = data[nameof(SkipHashCheck)]?.GetVal<bool>() ?? false;
this.InteractiveInstallation = data[nameof(InteractiveInstallation)]?.GetVal<bool>() ?? false;
this.RunAsAdministrator = data[nameof(RunAsAdministrator)]?.GetVal<bool>() ?? false;
this.Architecture = data[nameof(Architecture)]?.GetVal<string>() ?? "";
this.InstallationScope = data[nameof(InstallationScope)]?.GetVal<string>() ?? "";
foreach (var (stringKey, defValue) in DefaultStringValues)
SetValueToProperty(stringKey, data[stringKey]?.GetVal<string>() ?? defValue);

this.CustomParameters_Install = ReadArrayFromJson(data, nameof(CustomParameters_Install));
this.CustomParameters_Update = ReadArrayFromJson(data, nameof(CustomParameters_Update));
this.CustomParameters_Uninstall = ReadArrayFromJson(data, nameof(CustomParameters_Uninstall));
foreach (var listKey in DefaultListValues)
SetValueToProperty(listKey, ReadArrayFromJson(data, listKey));

// Handle case where setting has not been migrated yet
if (this.CustomParameters_Install.Count is 0 &&
this.CustomParameters_Update.Count is 0 &&
this.CustomParameters_Uninstall.Count is 0 &&
Expand All @@ -89,28 +127,42 @@ this.CustomParameters_Uninstall.Count is 0 &&
this.CustomParameters_Uninstall = ReadArrayFromJson(data, "CustomParameters");
}

this.KillBeforeOperation = ReadArrayFromJson(data, nameof(KillBeforeOperation));
this.PreRelease = data[nameof(PreRelease)]?.GetVal<bool>() ?? false;
this.CustomInstallLocation = data[nameof(CustomInstallLocation)]?.GetVal<string>() ?? "";
this.Version = data[nameof(Version)]?.GetVal<string>() ?? "";
this.SkipMinorUpdates = data[nameof(SkipMinorUpdates)]?.GetVal<bool>() ?? false;
this.UninstallPreviousVersionsOnUpdate = data[nameof(UninstallPreviousVersionsOnUpdate)]?.GetVal<bool>() ?? false;

this.PreInstallCommand = data[nameof(PreInstallCommand)]?.GetVal<string>() ?? "";
this.PreUpdateCommand = data[nameof(PreUpdateCommand)]?.GetVal<string>() ?? "";
this.PreUninstallCommand = data[nameof(PreUninstallCommand)]?.GetVal<string>() ?? "";
this.PostInstallCommand = data[nameof(PostInstallCommand)]?.GetVal<string>() ?? "";
this.PostUpdateCommand = data[nameof(PostUpdateCommand)]?.GetVal<string>() ?? "";
this.PostUninstallCommand = data[nameof(PostUninstallCommand)]?.GetVal<string>() ?? "";
this.AbortOnPreInstallFail = data[nameof(AbortOnPreInstallFail)]?.GetVal<bool>() ?? true;
this.AbortOnPreUpdateFail = data[nameof(AbortOnPreUpdateFail)]?.GetVal<bool>() ?? true;
this.AbortOnPreUninstallFail = data[nameof(AbortOnPreUninstallFail)]?.GetVal<bool>() ?? true;

// if OverridesNextLevelOpts is not found on the JSON, set it to true or false depending
// on whether the current settings instances are different from the default values.
// This entry shall be checked the last one, to ensure all other properties are set
this.OverridesNextLevelOpts =
data[nameof(OverridesNextLevelOpts)]?.GetValue<bool>() ?? DiffersFromDefault();
this.OverridesNextLevelOpts = data[nameof(OverridesNextLevelOpts)]?.GetValue<bool>() ?? DiffersFromDefault();
}

public override JsonNode AsJsonNode()
{
JsonObject obj = new();

if (OverridesNextLevelOpts is not false)
obj.Add(nameof(OverridesNextLevelOpts), OverridesNextLevelOpts);

foreach (var (boolKey, defValue) in DefaultBoolValues)
{
bool currentValue = GetValueFromProperty<bool>(boolKey);
if (currentValue != defValue) obj.Add(boolKey, currentValue);
}

foreach (var (stringKey, defValue) in DefaultStringValues)
{
string currentValue = GetValueFromProperty<string>(stringKey);
if (currentValue != defValue) obj.Add(stringKey, currentValue);
}

foreach (var listKey in DefaultListValues)
{
IReadOnlyList<string> currentValue = GetValueFromProperty<IReadOnlyList<string>>(listKey);

if (currentValue.Where(x => x.Any()).Any())
{
obj.Add(listKey, new JsonArray(currentValue.Select(x => JsonValue.Create(x) as JsonNode).ToArray()));
}
}

return obj;
}

private static List<string> ReadArrayFromJson(JsonNode data, string name)
Expand All @@ -124,30 +176,19 @@ private static List<string> ReadArrayFromJson(JsonNode data, string name)

public bool DiffersFromDefault()
{
return SkipHashCheck is not false ||
InteractiveInstallation is not false ||
RunAsAdministrator is not false ||
PreRelease is not false ||
SkipMinorUpdates is not false ||
Architecture.Any() ||
InstallationScope.Any() ||
CustomParameters_Install.Where(x => x.Any()).Any() ||
CustomParameters_Update.Where(x => x.Any()).Any() ||
CustomParameters_Uninstall.Where(x => x.Any()).Any() ||
KillBeforeOperation.Where(x => x.Any()).Any() ||
CustomInstallLocation.Any() ||
RemoveDataOnUninstall is not false ||
Version.Any() ||
PreInstallCommand.Any() ||
PostInstallCommand.Any() ||
AbortOnPreInstallFail is not true ||
PreUpdateCommand.Any() ||
PostUpdateCommand.Any() ||
AbortOnPreUpdateFail is not true ||
PreUninstallCommand.Any() ||
PostUninstallCommand.Any() ||
UninstallPreviousVersionsOnUpdate is not false ||
AbortOnPreUninstallFail is not true;
foreach (var (boolKey, defValue) in DefaultBoolValues)
if (GetValueFromProperty<bool>(boolKey) != defValue) return true;

foreach (var (stringKey, defValue) in DefaultStringValues)
if (GetValueFromProperty<string>(stringKey) != defValue) return true;

foreach (var listKey in DefaultListValues)
{
IReadOnlyList<string> currentValue = GetValueFromProperty<IReadOnlyList<string>>(listKey);
if (currentValue.Where(x => x.Any()).Any()) return true;
}

return false;
// OverridesNextLevelOpts does not need to be checked here, since
// this method is invoked before this property has been set
}
Expand Down
10 changes: 10 additions & 0 deletions src/UniGetUI.PackageEngine.Serializable/SerializableBundle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ public override void LoadFromJson(JsonNode data)
}
}

public override JsonNode AsJsonNode()
{
JsonObject obj = new();
obj.Add(nameof(export_version), export_version);
obj.Add(nameof(packages), new JsonArray(packages.Select(p => p.AsJsonNode()).ToArray()));
obj.Add(nameof(incompatible_packages_info), incompatible_packages_info);
Comment thread
marticliment marked this conversation as resolved.
obj.Add(nameof(incompatible_packages), new JsonArray(incompatible_packages.Select(p => p.AsJsonNode()).ToArray()));
return obj;
}

public SerializableBundle() : base()
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,14 @@ public abstract class SerializableComponent<T> where T: class
/// <returns>A pretty-formatted JSON string representing the current data</returns>
public string AsJsonString()
{
return JsonSerializer.Serialize<T>(this as T ?? throw new InvalidCastException("Invalid type"), SerializationHelpers.DefaultOptions);
return JsonSerializer.Serialize(AsJsonNode(), SerializationHelpers.DefaultOptions);
}

/// <summary>
/// Serializes this object into a JsonNode object
/// </summary>
/// <returns>A pretty-formatted JSON string representing the current data</returns>
public JsonNode AsJsonNode()
{
return JsonNode.Parse(AsJsonString()) ?? throw new InvalidDataException("The JSON object could not be parsed to a JsonNode");
}
public abstract JsonNode AsJsonNode();

/// <summary>
/// Creates an instance of this object with the default data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ public override void LoadFromJson(JsonNode data)
this.Source = data[nameof(Source)]?.GetVal<string>() ?? "";
}

public override JsonNode AsJsonNode()
{
JsonObject obj = new();
obj.Add(nameof(Id), Id);
obj.Add(nameof(Name), Name);
obj.Add(nameof(Version), Version);
obj.Add(nameof(Source), Source);
return obj;
}

public SerializableIncompatiblePackage(JsonNode data) : base(data)
{
}
Expand Down
13 changes: 13 additions & 0 deletions src/UniGetUI.PackageEngine.Serializable/SerializablePackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ public override void LoadFromJson(JsonNode data)
this.Updates = new(data[nameof(Updates)] ?? new JsonObject());
}

public override JsonNode AsJsonNode()
{
JsonObject obj = new();
obj.Add(nameof(Id), Id);
obj.Add(nameof(Name), Name);
obj.Add(nameof(Version), Version);
obj.Add(nameof(Source), Source);
obj.Add(nameof(ManagerName), ManagerName);
obj.Add(nameof(InstallationOptions), InstallationOptions.AsJsonNode());
obj.Add(nameof(Updates), Updates.AsJsonNode());
return obj;
}

public SerializablePackage() : base()
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ public override void LoadFromJson(JsonNode data)
this.IgnoredVersion = data[nameof(IgnoredVersion)]?.GetVal<string>() ?? "";
}

public override JsonNode AsJsonNode()
{
JsonObject obj = new();
if(UpdatesIgnored is not false) obj.Add(nameof(UpdatesIgnored), UpdatesIgnored);
if(IgnoredVersion.Any()) obj.Add(nameof(IgnoredVersion), IgnoredVersion);
return obj;
}

public SerializableUpdatesOptions() : base()
{
}
Expand Down
4 changes: 1 addition & 3 deletions src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -600,9 +600,7 @@ static int Comparison(IPackage x, IPackage y)
string ExportableData;

if (formatType is BundleFormatType.JSON or BundleFormatType.UBUNDLE)
ExportableData = JsonSerializer.Serialize(
exportable,
SerializationHelpers.DefaultOptions);
ExportableData = exportable.AsJsonString();

else if (formatType is BundleFormatType.YAML)
{
Expand Down
4 changes: 2 additions & 2 deletions src/UniGetUI/Services/GitHubBackupService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ namespace UniGetUI.Services
{
public class GitHubBackupService
{
private const string GistDescription_EndingKey = "#[UNIGETUI_BACKUP_V1]";
private const string PackageBackup_StartingKey = "#[PACKAGES]";
private const string GistDescription_EndingKey = "@[UNIGETUI_BACKUP_V1]";
private const string PackageBackup_StartingKey = "@[PACKAGES]";

private const string GistDescription = $"UniGetUI package backups - DO NOT RENAME OR MODIFY {GistDescription_EndingKey}";
private const string ReadMeContents = "" +
Expand Down
Loading