Skip to content

Commit 27184e1

Browse files
authored
Merge pull request #3832 from marticliment/json-bundles-improvements
2 parents 044c77c + eace2f0 commit 27184e1

9 files changed

Lines changed: 169 additions & 91 deletions

File tree

src/UniGetUI.Core.Tools/SerializationHelpers.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Globalization;
2+
using System.Text.Encodings.Web;
23
using System.Text.Json;
34
using System.Text.Json.Nodes;
45
using System.Text.Json.Serialization.Metadata;

src/UniGetUI.PackageEngine.Serializable/InstallOptions.cs

Lines changed: 122 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,48 @@
1+
using System.Text.Json;
12
using System.Text.Json.Nodes;
23
using UniGetUI.Core.Data;
34

45
namespace UniGetUI.PackageEngine.Serializable
56
{
67
public class InstallOptions: SerializableComponent<InstallOptions>
78
{
9+
private readonly IReadOnlyDictionary<string, bool> DefaultBoolValues = new Dictionary<string, bool>()
10+
{ // OverridesNextLevelOpts is deliberately skipped here
11+
{ "SkipHashCheck", false },
12+
{ "InteractiveInstallation", false },
13+
{ "RunAsAdministrator", false },
14+
{ "PreRelease", false },
15+
{ "SkipMinorUpdates", false },
16+
{ "RemoveDataOnUninstall", false },
17+
{ "UninstallPreviousVersionsOnUpdate", false },
18+
{ "AbortOnPreInstallFail", true },
19+
{ "AbortOnPreUpdateFail", true },
20+
{ "AbortOnPreUninstallFail", true },
21+
};
22+
23+
private readonly IReadOnlyDictionary<string, string> DefaultStringValues = new Dictionary<string, string>()
24+
{
25+
{"Architecture", ""},
26+
{"InstallationScope", ""},
27+
{"CustomInstallLocation", ""},
28+
{"Version", ""},
29+
{"PreInstallCommand", ""},
30+
{"PostInstallCommand", ""},
31+
{"PreUpdateCommand", ""},
32+
{"PostUpdateCommand", ""},
33+
{"PreUninstallCommand", ""},
34+
{"PostUninstallCommand", ""},
35+
};
36+
37+
private readonly IReadOnlyList<string> DefaultListValues = new List<string>()
38+
{
39+
"CustomParameters_Install",
40+
"CustomParameters_Update",
41+
"CustomParameters_Uninstall",
42+
"KillBeforeOperation",
43+
};
44+
45+
846
public bool SkipHashCheck { get; set; }
947
public bool InteractiveInstallation { get; set; }
1048
public bool RunAsAdministrator { get; set; }
@@ -17,7 +55,6 @@ public class InstallOptions: SerializableComponent<InstallOptions>
1755
public string CustomInstallLocation { get; set; } = "";
1856
public string Version { get; set; } = "";
1957
public bool SkipMinorUpdates { get; set; }
20-
public bool OverridesNextLevelOpts { get; set; }
2158
public bool RemoveDataOnUninstall { get; set; }
2259
public bool UninstallPreviousVersionsOnUpdate { get; set; }
2360
public List<string> KillBeforeOperation { get; set; } = [];
@@ -32,53 +69,54 @@ public class InstallOptions: SerializableComponent<InstallOptions>
3269
public string PostUninstallCommand { get; set; } = "";
3370
public bool AbortOnPreUninstallFail { get; set; } = true;
3471

72+
public bool OverridesNextLevelOpts { get; set; }
3573

3674
public override InstallOptions Copy()
3775
{
38-
return new()
39-
{
40-
SkipHashCheck = SkipHashCheck,
41-
Architecture = Architecture,
42-
CustomInstallLocation = CustomInstallLocation,
43-
CustomParameters_Install = CustomParameters_Install.ToList(),
44-
CustomParameters_Update = CustomParameters_Update.ToList(),
45-
CustomParameters_Uninstall = CustomParameters_Uninstall.ToList(),
46-
InstallationScope = InstallationScope,
47-
InteractiveInstallation = InteractiveInstallation,
48-
PreRelease = PreRelease,
49-
RunAsAdministrator = RunAsAdministrator,
50-
Version = Version,
51-
SkipMinorUpdates = SkipMinorUpdates,
52-
OverridesNextLevelOpts = OverridesNextLevelOpts,
53-
RemoveDataOnUninstall = RemoveDataOnUninstall,
54-
KillBeforeOperation = KillBeforeOperation.ToList(),
55-
PreInstallCommand = PreInstallCommand,
56-
PreUpdateCommand = PreUpdateCommand,
57-
PreUninstallCommand = PreUninstallCommand,
58-
PostInstallCommand = PostInstallCommand,
59-
PostUpdateCommand = PostUpdateCommand,
60-
PostUninstallCommand = PostUninstallCommand,
61-
AbortOnPreInstallFail = AbortOnPreInstallFail,
62-
AbortOnPreUpdateFail = AbortOnPreUpdateFail,
63-
AbortOnPreUninstallFail = AbortOnPreUninstallFail,
64-
UninstallPreviousVersionsOnUpdate = UninstallPreviousVersionsOnUpdate,
65-
};
76+
var copy = new InstallOptions();
77+
78+
foreach (var (boolKey, _) in DefaultBoolValues)
79+
copy.SetValueToProperty(boolKey, GetValueFromProperty<bool>(boolKey));
80+
81+
foreach (var (stringKey, _) in DefaultStringValues)
82+
copy.SetValueToProperty(stringKey, GetValueFromProperty<string>(stringKey));
83+
84+
foreach (var listKey in DefaultListValues)
85+
copy.SetValueToProperty(listKey, GetValueFromProperty<IReadOnlyList<string>>(listKey).ToList());
86+
87+
// Handle non-automated OverridesNextLevelOpts
88+
copy.OverridesNextLevelOpts = OverridesNextLevelOpts;
89+
return copy;
90+
}
91+
92+
public void SetValueToProperty<T>(string name, T value)
93+
{
94+
var property = this.GetType().GetProperty(name);
95+
property?.SetValue(this, value);
96+
}
97+
98+
public T GetValueFromProperty<T>(string name)
99+
{
100+
var property = this.GetType().GetProperty(name);
101+
return (T)(property?.GetValue(this) ?? throw new InvalidDataException($"Invalid datatype for property {name} (expected {nameof(T)})"));
66102
}
67103

68104
public override void LoadFromJson(JsonNode data)
69105
{
70-
// RemoveDataOnUninstall should not be loaded from disk
106+
foreach (var (boolKey, defValue) in DefaultBoolValues)
107+
{
108+
// RemoveDataOnUninstall should not be loaded from disk
109+
if(boolKey == "RemoveDataOnUninstall") continue;
110+
SetValueToProperty(boolKey, data[boolKey]?.GetVal<bool>() ?? defValue);
111+
}
71112

72-
this.SkipHashCheck = data[nameof(SkipHashCheck)]?.GetVal<bool>() ?? false;
73-
this.InteractiveInstallation = data[nameof(InteractiveInstallation)]?.GetVal<bool>() ?? false;
74-
this.RunAsAdministrator = data[nameof(RunAsAdministrator)]?.GetVal<bool>() ?? false;
75-
this.Architecture = data[nameof(Architecture)]?.GetVal<string>() ?? "";
76-
this.InstallationScope = data[nameof(InstallationScope)]?.GetVal<string>() ?? "";
113+
foreach (var (stringKey, defValue) in DefaultStringValues)
114+
SetValueToProperty(stringKey, data[stringKey]?.GetVal<string>() ?? defValue);
77115

78-
this.CustomParameters_Install = ReadArrayFromJson(data, nameof(CustomParameters_Install));
79-
this.CustomParameters_Update = ReadArrayFromJson(data, nameof(CustomParameters_Update));
80-
this.CustomParameters_Uninstall = ReadArrayFromJson(data, nameof(CustomParameters_Uninstall));
116+
foreach (var listKey in DefaultListValues)
117+
SetValueToProperty(listKey, ReadArrayFromJson(data, listKey));
81118

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

92-
this.KillBeforeOperation = ReadArrayFromJson(data, nameof(KillBeforeOperation));
93-
this.PreRelease = data[nameof(PreRelease)]?.GetVal<bool>() ?? false;
94-
this.CustomInstallLocation = data[nameof(CustomInstallLocation)]?.GetVal<string>() ?? "";
95-
this.Version = data[nameof(Version)]?.GetVal<string>() ?? "";
96-
this.SkipMinorUpdates = data[nameof(SkipMinorUpdates)]?.GetVal<bool>() ?? false;
97-
this.UninstallPreviousVersionsOnUpdate = data[nameof(UninstallPreviousVersionsOnUpdate)]?.GetVal<bool>() ?? false;
98-
99-
this.PreInstallCommand = data[nameof(PreInstallCommand)]?.GetVal<string>() ?? "";
100-
this.PreUpdateCommand = data[nameof(PreUpdateCommand)]?.GetVal<string>() ?? "";
101-
this.PreUninstallCommand = data[nameof(PreUninstallCommand)]?.GetVal<string>() ?? "";
102-
this.PostInstallCommand = data[nameof(PostInstallCommand)]?.GetVal<string>() ?? "";
103-
this.PostUpdateCommand = data[nameof(PostUpdateCommand)]?.GetVal<string>() ?? "";
104-
this.PostUninstallCommand = data[nameof(PostUninstallCommand)]?.GetVal<string>() ?? "";
105-
this.AbortOnPreInstallFail = data[nameof(AbortOnPreInstallFail)]?.GetVal<bool>() ?? true;
106-
this.AbortOnPreUpdateFail = data[nameof(AbortOnPreUpdateFail)]?.GetVal<bool>() ?? true;
107-
this.AbortOnPreUninstallFail = data[nameof(AbortOnPreUninstallFail)]?.GetVal<bool>() ?? true;
108-
109130
// if OverridesNextLevelOpts is not found on the JSON, set it to true or false depending
110131
// on whether the current settings instances are different from the default values.
111132
// This entry shall be checked the last one, to ensure all other properties are set
112-
this.OverridesNextLevelOpts =
113-
data[nameof(OverridesNextLevelOpts)]?.GetValue<bool>() ?? DiffersFromDefault();
133+
this.OverridesNextLevelOpts = data[nameof(OverridesNextLevelOpts)]?.GetValue<bool>() ?? DiffersFromDefault();
134+
}
135+
136+
public override JsonNode AsJsonNode()
137+
{
138+
JsonObject obj = new();
139+
140+
if (OverridesNextLevelOpts is not false)
141+
obj.Add(nameof(OverridesNextLevelOpts), OverridesNextLevelOpts);
142+
143+
foreach (var (boolKey, defValue) in DefaultBoolValues)
144+
{
145+
bool currentValue = GetValueFromProperty<bool>(boolKey);
146+
if (currentValue != defValue) obj.Add(boolKey, currentValue);
147+
}
148+
149+
foreach (var (stringKey, defValue) in DefaultStringValues)
150+
{
151+
string currentValue = GetValueFromProperty<string>(stringKey);
152+
if (currentValue != defValue) obj.Add(stringKey, currentValue);
153+
}
154+
155+
foreach (var listKey in DefaultListValues)
156+
{
157+
IReadOnlyList<string> currentValue = GetValueFromProperty<IReadOnlyList<string>>(listKey);
158+
159+
if (currentValue.Where(x => x.Any()).Any())
160+
{
161+
obj.Add(listKey, new JsonArray(currentValue.Select(x => JsonValue.Create(x) as JsonNode).ToArray()));
162+
}
163+
}
164+
165+
return obj;
114166
}
115167

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

125177
public bool DiffersFromDefault()
126178
{
127-
return SkipHashCheck is not false ||
128-
InteractiveInstallation is not false ||
129-
RunAsAdministrator is not false ||
130-
PreRelease is not false ||
131-
SkipMinorUpdates is not false ||
132-
Architecture.Any() ||
133-
InstallationScope.Any() ||
134-
CustomParameters_Install.Where(x => x.Any()).Any() ||
135-
CustomParameters_Update.Where(x => x.Any()).Any() ||
136-
CustomParameters_Uninstall.Where(x => x.Any()).Any() ||
137-
KillBeforeOperation.Where(x => x.Any()).Any() ||
138-
CustomInstallLocation.Any() ||
139-
RemoveDataOnUninstall is not false ||
140-
Version.Any() ||
141-
PreInstallCommand.Any() ||
142-
PostInstallCommand.Any() ||
143-
AbortOnPreInstallFail is not true ||
144-
PreUpdateCommand.Any() ||
145-
PostUpdateCommand.Any() ||
146-
AbortOnPreUpdateFail is not true ||
147-
PreUninstallCommand.Any() ||
148-
PostUninstallCommand.Any() ||
149-
UninstallPreviousVersionsOnUpdate is not false ||
150-
AbortOnPreUninstallFail is not true;
179+
foreach (var (boolKey, defValue) in DefaultBoolValues)
180+
if (GetValueFromProperty<bool>(boolKey) != defValue) return true;
181+
182+
foreach (var (stringKey, defValue) in DefaultStringValues)
183+
if (GetValueFromProperty<string>(stringKey) != defValue) return true;
184+
185+
foreach (var listKey in DefaultListValues)
186+
{
187+
IReadOnlyList<string> currentValue = GetValueFromProperty<IReadOnlyList<string>>(listKey);
188+
if (currentValue.Where(x => x.Any()).Any()) return true;
189+
}
190+
191+
return false;
151192
// OverridesNextLevelOpts does not need to be checked here, since
152193
// this method is invoked before this property has been set
153194
}

src/UniGetUI.PackageEngine.Serializable/SerializableBundle.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ public override void LoadFromJson(JsonNode data)
5858
}
5959
}
6060

61+
public override JsonNode AsJsonNode()
62+
{
63+
JsonObject obj = new();
64+
obj.Add(nameof(export_version), export_version);
65+
obj.Add(nameof(packages), new JsonArray(packages.Select(p => p.AsJsonNode()).ToArray()));
66+
obj.Add(nameof(incompatible_packages_info), incompatible_packages_info);
67+
obj.Add(nameof(incompatible_packages), new JsonArray(incompatible_packages.Select(p => p.AsJsonNode()).ToArray()));
68+
return obj;
69+
}
70+
6171
public SerializableBundle() : base()
6272
{
6373
}

src/UniGetUI.PackageEngine.Serializable/SerializableComponent.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,14 @@ public abstract class SerializableComponent<T> where T: class
2424
/// <returns>A pretty-formatted JSON string representing the current data</returns>
2525
public string AsJsonString()
2626
{
27-
return JsonSerializer.Serialize<T>(this as T ?? throw new InvalidCastException("Invalid type"), SerializationHelpers.DefaultOptions);
27+
return JsonSerializer.Serialize(AsJsonNode(), SerializationHelpers.DefaultOptions);
2828
}
2929

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

3936
/// <summary>
4037
/// Creates an instance of this object with the default data

src/UniGetUI.PackageEngine.Serializable/SerializableIncompatiblePackage.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ public override void LoadFromJson(JsonNode data)
2727
this.Source = data[nameof(Source)]?.GetVal<string>() ?? "";
2828
}
2929

30+
public override JsonNode AsJsonNode()
31+
{
32+
JsonObject obj = new();
33+
obj.Add(nameof(Id), Id);
34+
obj.Add(nameof(Name), Name);
35+
obj.Add(nameof(Version), Version);
36+
obj.Add(nameof(Source), Source);
37+
return obj;
38+
}
39+
3040
public SerializableIncompatiblePackage(JsonNode data) : base(data)
3141
{
3242
}

src/UniGetUI.PackageEngine.Serializable/SerializablePackage.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ public override void LoadFromJson(JsonNode data)
4141
this.Updates = new(data[nameof(Updates)] ?? new JsonObject());
4242
}
4343

44+
public override JsonNode AsJsonNode()
45+
{
46+
JsonObject obj = new();
47+
obj.Add(nameof(Id), Id);
48+
obj.Add(nameof(Name), Name);
49+
obj.Add(nameof(Version), Version);
50+
obj.Add(nameof(Source), Source);
51+
obj.Add(nameof(ManagerName), ManagerName);
52+
obj.Add(nameof(InstallationOptions), InstallationOptions.AsJsonNode());
53+
obj.Add(nameof(Updates), Updates.AsJsonNode());
54+
return obj;
55+
}
56+
4457
public SerializablePackage() : base()
4558
{
4659
}

src/UniGetUI.PackageEngine.Serializable/SerializableUpdatesOptions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ public override void LoadFromJson(JsonNode data)
2020
this.IgnoredVersion = data[nameof(IgnoredVersion)]?.GetVal<string>() ?? "";
2121
}
2222

23+
public override JsonNode AsJsonNode()
24+
{
25+
JsonObject obj = new();
26+
if(UpdatesIgnored is not false) obj.Add(nameof(UpdatesIgnored), UpdatesIgnored);
27+
if(IgnoredVersion.Any()) obj.Add(nameof(IgnoredVersion), IgnoredVersion);
28+
return obj;
29+
}
30+
2331
public SerializableUpdatesOptions() : base()
2432
{
2533
}

src/UniGetUI/Pages/SoftwarePages/PackageBundlesPage.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -600,9 +600,7 @@ static int Comparison(IPackage x, IPackage y)
600600
string ExportableData;
601601

602602
if (formatType is BundleFormatType.JSON or BundleFormatType.UBUNDLE)
603-
ExportableData = JsonSerializer.Serialize(
604-
exportable,
605-
SerializationHelpers.DefaultOptions);
603+
ExportableData = exportable.AsJsonString();
606604

607605
else if (formatType is BundleFormatType.YAML)
608606
{

src/UniGetUI/Services/GitHubBackupService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ namespace UniGetUI.Services
66
{
77
public class GitHubBackupService
88
{
9-
private const string GistDescription_EndingKey = "#[UNIGETUI_BACKUP_V1]";
10-
private const string PackageBackup_StartingKey = "#[PACKAGES]";
9+
private const string GistDescription_EndingKey = "@[UNIGETUI_BACKUP_V1]";
10+
private const string PackageBackup_StartingKey = "@[PACKAGES]";
1111

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

0 commit comments

Comments
 (0)