-
Notifications
You must be signed in to change notification settings - Fork 788
Expand file tree
/
Copy pathPackage.cs
More file actions
353 lines (307 loc) · 12.9 KB
/
Package.cs
File metadata and controls
353 lines (307 loc) · 12.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using UniGetUI.Core.Classes;
using UniGetUI.Core.IconEngine;
using UniGetUI.Core.Logging;
using UniGetUI.Core.Tools;
using UniGetUI.Interface.Enums;
using UniGetUI.PackageEngine.Classes.Packages;
using UniGetUI.PackageEngine.Classes.Packages.Classes;
using UniGetUI.PackageEngine.Classes.Serializable;
using UniGetUI.PackageEngine.Interfaces;
using UniGetUI.PackageEngine.Structs;
namespace UniGetUI.PackageEngine.PackageClasses
{
public class Package : IPackage
{
// Internal properties
private bool __is_checked;
public event PropertyChangedEventHandler? PropertyChanged;
private PackageTag __tag;
private readonly long __hash;
private readonly long __versioned_hash;
private readonly string ignoredId;
private readonly string _iconId;
private static readonly ConcurrentDictionary<int, Uri?> _cachedIconPaths = new();
private IPackageDetails? __details;
public IPackageDetails Details
{
get => __details ??= new PackageDetails(this);
}
public PackageTag Tag
{
get => __tag;
set { __tag = value; OnPropertyChanged(); }
}
public bool IsChecked
{
get => __is_checked;
set { __is_checked = value; OnPropertyChanged(); }
}
private OverridenInstallationOptions _overriden_options;
public ref OverridenInstallationOptions OverridenOptions { get => ref _overriden_options; }
public string Name { get; }
public string AutomationName { get; }
public string Id { get; }
public virtual string VersionString { get; }
public CoreTools.Version NormalizedVersion { get; }
public CoreTools.Version NormalizedNewVersion { get; }
public bool IsPopulated { get; set; }
public IManagerSource Source { get; }
/// <summary>
/// IPackageManager is guaranteed to be PackageManager, but C# doesn't allow covariant attributes
/// </summary>
public IPackageManager Manager { get; }
public string NewVersionString { get; }
public virtual bool IsUpgradable { get; }
/// <summary>
/// Construct a package with a given name, id, version, source and manager, and an optional scope.
/// </summary>
public Package(
string name,
string id,
string version,
IManagerSource source,
IPackageManager manager,
OverridenInstallationOptions? options = null)
{
Name = name;
Id = id;
VersionString = version;
NormalizedVersion = CoreTools.VersionStringToStruct(version);
Source = source;
Manager = manager;
if (options is not null)
{
_overriden_options = (OverridenInstallationOptions)options;
}
NewVersionString = "";
Tag = PackageTag.Default;
AutomationName = CoreTools.Translate("Package {name} from {manager}",
new Dictionary<string, object?> { { "name", Name }, { "manager", Source.AsString_DisplayName } });
__hash = CoreTools.HashStringAsLong(Manager.Name + "\\" + Source.AsString_DisplayName + "\\" + Id);
__versioned_hash = CoreTools.HashStringAsLong(Manager.Name + "\\" + Source.AsString_DisplayName + "\\" + Id + "\\" + (this as Package).VersionString);
IsUpgradable = false;
ignoredId = IgnoredUpdatesDatabase.GetIgnoredIdForPackage(this);
_iconId = GetPackageIconId(id, Manager.Name, Source.Name);
}
public static string GetPackageIconId(string PackageId, string ManagerName, string SourceName)
{
return ManagerName switch
{
"Winget" => SourceName switch
{
"Steam" => PackageId.ToLower().Split("\\")[^1].Replace("steam app ", "steam-").Trim(),
"Local PC" => PackageId.ToLower().Split("\\")[^1],
"Microsoft Store" => PackageId.IndexOf('_') < PackageId.IndexOf('.') ? // If the first underscore is before the period, this ID has no publisher
string.Join('_', PackageId.ToLower().Split("\\")[1].Split("_")[0..^4]) : // no publisher: remove `MSIX\`, then the standard ending _version_arch__{random id}
string.Join('_', string.Join('.', PackageId.ToLower().Split(".")[1..]).Split("_")[0..^4]), // remove the publisher (before the first .), then the standard _version_arch__{random id}
_ => string.Join('.', PackageId.ToLower().Split(".")[1..]),
},
"Scoop" => PackageId.ToLower().Replace(".app", ""),
"Chocolatey" => PackageId.ToLower().Replace(".install", "").Replace(".portable", ""),
"vcpkg" => PackageId.ToLower().Split(":")[0].Split("[")[0],
_ => PackageId.ToLower()
};
}
/// <summary>
/// Creates an UpgradablePackage object representing a package that can be upgraded; given its name, id, installed version, new version, source and manager, and an optional scope.
/// </summary>
public Package(
string name,
string id,
string installed_version,
string new_version,
IManagerSource source,
IPackageManager manager,
OverridenInstallationOptions? options = null)
: this(name, id, installed_version, source, manager, options)
{
IsUpgradable = true;
NewVersionString = new_version;
NormalizedNewVersion = CoreTools.VersionStringToStruct(new_version);
}
public long GetHash()
=> __hash;
public long GetVersionedHash()
=> __versioned_hash;
public bool Equals(IPackage? other)
=> __versioned_hash == other?.GetHash();
public override int GetHashCode()
=> (int)__versioned_hash;
/// <summary>
/// Check whether two package instances represent the same package.
/// What is taken into account:
/// - Manager and Source
/// - Package Identifier
/// For more specific comparison use package.Equals(object? other)
/// </summary>
/// <param name="other">A package</param>
/// <returns>Whether the two instances refer to the same instance</returns>
public bool IsEquivalentTo(IPackage? other)
=> __hash == other?.GetHash();
public string GetIconId()
=> _iconId;
public virtual Uri GetIconUrl()
{
return GetIconUrlIfAny() ?? new Uri("ms-appx:///Assets/Images/package_color.png");
}
public virtual Uri? GetIconUrlIfAny()
{
if (_cachedIconPaths.TryGetValue(this.GetHashCode(), out Uri? path))
{
return path;
}
var CachedIcon = LoadIconUrlIfAny();
_cachedIconPaths.TryAdd(this.GetHashCode(), CachedIcon);
return CachedIcon;
}
private Uri? LoadIconUrlIfAny()
{
try
{
CacheableIcon? icon = TaskRecycler<CacheableIcon?>.RunOrAttach(Manager.DetailsHelper.GetIcon, this);
string? path = IconCacheEngine.GetCacheOrDownloadIcon(icon, Manager.Name, _iconId);
return path is null? null: new Uri("file:///" + path);
}
catch (Exception ex)
{
Logger.Error($"An error occurred while retrieving the icon for package {Id}");
Logger.Error(ex);
return null;
}
}
public virtual IReadOnlyList<Uri> GetScreenshots()
{
return Manager.DetailsHelper.GetScreenshots(this);
}
public virtual async Task AddToIgnoredUpdatesAsync(string version = "*")
{
try
{
await Task.Run(() => IgnoredUpdatesDatabase.Add(ignoredId, version));
GetInstalledPackage()?.SetTag(PackageTag.Pinned);
}
catch (Exception ex)
{
Logger.Error($"Could not add package {Id} to ignored updates");
Logger.Error(ex);
}
}
public virtual async Task RemoveFromIgnoredUpdatesAsync()
{
try
{
await Task.Run(() => IgnoredUpdatesDatabase.Remove(ignoredId));
GetInstalledPackage()?.SetTag(PackageTag.Default);
}
catch (Exception ex)
{
Logger.Error($"Could not remove package {Id} from ignored updates");
Logger.Error(ex);
}
}
/// <summary>
/// Returns true if the package's updates are ignored. If the version parameter
/// is passed it will be checked if that version is ignored. Please note that if
/// all updates are ignored, calling this method with a specific version will
/// still return true, although the passed version is not explicitly ignored.
/// </summary>
public virtual async Task<bool> HasUpdatesIgnoredAsync(string version = "*")
{
try
{
return await Task.Run(() => IgnoredUpdatesDatabase.HasUpdatesIgnored(ignoredId, version));
}
catch (Exception ex)
{
Logger.Error($"Could not check whether package {Id} has updates ignored");
Logger.Error(ex);
return false;
}
}
/// <summary>
/// Returns (as a string) the version for which a package has been ignored. When no versions
/// are ignored, an empty string will be returned; and when all versions are ignored an asterisk
/// will be returned.
/// </summary>
public virtual async Task<string> GetIgnoredUpdatesVersionAsync()
{
try
{
return await Task.Run(() => IgnoredUpdatesDatabase.GetIgnoredVersion(ignoredId)) ?? "";
}
catch (Exception ex)
{
Logger.Error($"Could not retrieve the ignored updates version for package {Id}");
Logger.Error(ex);
return "";
}
}
protected void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public IPackage? GetInstalledPackage()
=> PackageCacher.GetInstalledPackageOrNull(this);
public IPackage? GetAvailablePackage()
=> PackageCacher.GetAvailablePackageOrNull(this);
public IPackage? GetUpgradablePackage()
=> PackageCacher.GetUpgradablePackageOrNull(this);
public virtual void SetTag(PackageTag tag)
{
Tag = tag;
}
public virtual bool NewerVersionIsInstalled()
{
if (!IsUpgradable)
return false;
return PackageCacher.NewerVersionIsInstalled(this);
}
public virtual bool IsUpdateMinor()
{
if (!IsUpgradable) return false;
string[] VersionSplit = VersionString.Split(".");
string[] NewVersionSplit = NewVersionString.Split(".");
// When in doubt, return false
if (VersionSplit.Length < 3 || NewVersionSplit.Length < 3) return false;
if (
VersionSplit[0] != NewVersionSplit[0] ||
VersionSplit[1] != NewVersionSplit[1]
) return false; // Major update
return VersionSplit[2].CompareTo(NewVersionSplit[2]) < 0;
}
public virtual SerializablePackage_v1 AsSerializable()
{
return new SerializablePackage_v1
{
Id = Id,
Name = Name,
Version = VersionString,
Source = Source.Name,
ManagerName = Manager.Name,
InstallationOptions = InstallationOptions.FromPackage(this).AsSerializable(),
Updates = new SerializableUpdatesOptions_v1
{
IgnoredVersion = GetIgnoredUpdatesVersionAsync().GetAwaiter().GetResult(),
UpdatesIgnored = HasUpdatesIgnoredAsync().GetAwaiter().GetResult(),
}
};
}
public SerializableIncompatiblePackage_v1 AsSerializable_Incompatible()
{
return new SerializableIncompatiblePackage_v1
{
Id = Id,
Name = Name,
Version = VersionString,
Source = Source.Name,
};
}
public static void ResetIconCache()
{
_cachedIconPaths.Clear();
}
}
}