Skip to content

Commit b03a6f5

Browse files
committed
Lay ground to be able to run multiple operations before and after an operation
1 parent eb1dca9 commit b03a6f5

7 files changed

Lines changed: 295 additions & 177 deletions

File tree

src/UniGetUI.PackageEngine.Operations/AbstractOperation.cs

Lines changed: 108 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -2,67 +2,13 @@
22
using UniGetUI.Core.SettingsEngine;
33
using UniGetUI.Core.Tools;
44
using UniGetUI.PackageEngine.Enums;
5+
using YamlDotNet.Serialization.ObjectGraphVisitors;
56

67
namespace UniGetUI.PackageOperations;
78

8-
public abstract class AbstractOperation : IDisposable
9+
public abstract partial class AbstractOperation : IDisposable
910
{
10-
public static class RetryMode
11-
{
12-
public const string NoRetry = "";
13-
public const string Retry = "Retry";
14-
public const string Retry_AsAdmin = "RetryAsAdmin";
15-
public const string Retry_Interactive = "RetryInteractive";
16-
public const string Retry_SkipIntegrity = "RetryNoHashCheck";
17-
}
18-
19-
public class OperationMetadata
20-
{
21-
/// <summary>
22-
/// Installation of X
23-
/// </summary>
24-
public string Title = "";
25-
26-
/// <summary>
27-
/// X is being installed/upated/removed
28-
/// </summary>
29-
public string Status = "";
30-
31-
/// <summary>
32-
/// X was installed
33-
/// </summary>
34-
public string SuccessTitle = "";
35-
36-
/// <summary>
37-
/// X has been installed successfully
38-
/// </summary>
39-
public string SuccessMessage = "";
40-
41-
/// <summary>
42-
/// X could not be installed.
43-
/// </summary>
44-
public string FailureTitle = "";
45-
46-
/// <summary>
47-
/// X Could not be installed
48-
/// </summary>
49-
public string FailureMessage = "";
50-
51-
/// <summary>
52-
/// Starting operation X with options Y
53-
/// </summary>
54-
public string OperationInformation = "";
55-
56-
public readonly string Identifier;
57-
58-
public OperationMetadata()
59-
{
60-
Identifier = new Random().NextInt64(1000000, 9999999).ToString();
61-
}
62-
}
63-
6411
public readonly OperationMetadata Metadata = new();
65-
public static readonly List<AbstractOperation> OperationQueue = [];
6612

6713
public event EventHandler<OperationStatus>? StatusChanged;
6814
public event EventHandler<EventArgs>? CancelRequested;
@@ -72,38 +18,12 @@ public OperationMetadata()
7218
public event EventHandler<EventArgs>? Enqueued;
7319
public event EventHandler<EventArgs>? OperationSucceeded;
7420
public event EventHandler<EventArgs>? OperationFailed;
75-
76-
public static int MAX_OPERATIONS;
77-
7821
public event EventHandler<BadgeCollection>? BadgesChanged;
7922

80-
public class BadgeCollection
81-
{
82-
public readonly bool AsAdministrator;
83-
public readonly bool Interactive;
84-
public readonly bool SkipHashCheck;
85-
public readonly string? Scope;
86-
87-
public BadgeCollection(bool admin, bool interactive, bool skiphash, string? scope)
88-
{
89-
AsAdministrator = admin;
90-
Interactive = interactive;
91-
SkipHashCheck = skiphash;
92-
Scope = scope;
93-
}
94-
}
95-
public void ApplyCapabilities(bool admin, bool interactive, bool skiphash, string? scope)
96-
{
97-
BadgesChanged?.Invoke(this, new BadgeCollection(admin, interactive, skiphash, scope));
98-
}
99-
100-
public enum LineType
101-
{
102-
VerboseDetails,
103-
ProgressIndicator,
104-
Information,
105-
Error
106-
}
23+
public bool Started { get; private set; }
24+
protected bool QUEUE_ENABLED;
25+
protected bool FORCE_HOLD_QUEUE;
26+
private bool IsInnerOperation = false;
10727

10828
private readonly List<(string, LineType)> LogList = [];
10929
private OperationStatus _status = OperationStatus.InQueue;
@@ -113,20 +33,23 @@ public OperationStatus Status
11333
set { _status = value; StatusChanged?.Invoke(this, value); }
11434
}
11535

116-
public bool Started { get; private set; }
117-
protected bool QUEUE_ENABLED;
118-
protected bool FORCE_HOLD_QUEUE;
36+
public void ApplyCapabilities(bool admin, bool interactive, bool skiphash, string? scope)
37+
{
38+
BadgesChanged?.Invoke(this, new BadgeCollection(admin, interactive, skiphash, scope));
39+
}
11940

120-
private readonly AbstractOperation? requirement;
41+
// private readonly AbstractOperation? requirement;
42+
private readonly IReadOnlyList<InnerOperation> PreOperations = [];
43+
private readonly IReadOnlyList<InnerOperation> PostOperations = [];
12144

122-
public AbstractOperation(bool queue_enabled, AbstractOperation? req)
45+
public AbstractOperation(
46+
bool queue_enabled,
47+
IReadOnlyList<InnerOperation>? preOps = null,
48+
IReadOnlyList<InnerOperation>? postOps = null)
12349
{
12450
QUEUE_ENABLED = queue_enabled;
125-
if (req is not null)
126-
{
127-
requirement = req;
128-
QUEUE_ENABLED = false;
129-
}
51+
if (preOps is not null) PreOperations = preOps;
52+
if (postOps is not null) PostOperations = postOps;
13053

13154
Status = OperationStatus.InQueue;
13255
Line("Please wait...", LineType.ProgressIndicator);
@@ -202,20 +125,9 @@ public async Task MainThread()
202125

203126
Enqueued?.Invoke(this, EventArgs.Empty);
204127

205-
if (requirement != null)
206-
{ // OPERATION REQUIREMENT HANDLER
207-
Logger.Info($"Operation {Metadata.Title} is waiting for requirement operation {requirement.Metadata.Title}");
208-
Line(CoreTools.Translate("Waiting for {0} to complete...", requirement.Metadata.Title), LineType.ProgressIndicator);
209-
{
210-
while (requirement.Status is OperationStatus.Running or OperationStatus.InQueue)
211-
{
212-
await Task.Delay(100);
213-
if (SKIP_QUEUE) break;
214-
}
215-
}
216-
}
217-
else if (QUEUE_ENABLED)
218-
{ // QUEUE HANDLER
128+
if (QUEUE_ENABLED && !IsInnerOperation)
129+
{
130+
// QUEUE HANDLER
219131
SKIP_QUEUE = false;
220132
OperationQueue.Add(this);
221133
int lastPos = -2;
@@ -238,61 +150,8 @@ public async Task MainThread()
238150
}
239151
// END QUEUE HANDLER
240152

241-
// BEGIN ACTUAL OPERATION
242-
OperationVeredict result;
243-
Line(CoreTools.Translate("Starting operation..."), LineType.ProgressIndicator);
244-
if (Status is OperationStatus.InQueue) Status = OperationStatus.Running;
245-
246-
do
247-
{
248-
OperationStarting?.Invoke(this, EventArgs.Empty);
249-
250-
try
251-
{
252-
// Check if the operation was canceled
253-
if (Status is OperationStatus.Canceled)
254-
{
255-
result = OperationVeredict.Canceled;
256-
break;
257-
}
258-
259-
if (requirement is not null)
260-
{
261-
if (requirement.Status is OperationStatus.Failed)
262-
{
263-
Line(CoreTools.Translate("{0} has failed, that was a requirement for {1} to be run", requirement.Metadata.Title, Metadata.Title), LineType.Error);
264-
result = OperationVeredict.Failure;
265-
break;
266-
}
267-
268-
if (requirement.Status is OperationStatus.Canceled)
269-
{
270-
Line(CoreTools.Translate("The user has canceled {0}, that was a requirement for {1} to be run", requirement.Metadata.Title, Metadata.Title), LineType.Error);
271-
result = OperationVeredict.Canceled;
272-
break;
273-
}
274-
}
275-
276-
Task<OperationVeredict> op = PerformOperation();
277-
while (Status != OperationStatus.Canceled && !op.IsCompleted) await Task.Delay(100);
278-
279-
if (Status is OperationStatus.Canceled) result = OperationVeredict.Canceled;
280-
else result = op.GetAwaiter().GetResult();
281-
}
282-
catch (Exception e)
283-
{
284-
result = OperationVeredict.Failure;
285-
Logger.Error(e);
286-
foreach (string l in e.ToString().Split("\n"))
287-
{
288-
Line(l, LineType.Error);
289-
}
290-
}
291-
} while (result == OperationVeredict.AutoRetry);
292-
293-
153+
var result = await _runOperation();
294154
while (OperationQueue.Remove(this));
295-
// END OPERATION
296155

297156
if (result == OperationVeredict.Success)
298157
{
@@ -352,6 +211,91 @@ public async Task MainThread()
352211
}
353212
}
354213

214+
private async Task<OperationVeredict> _runOperation()
215+
{
216+
OperationVeredict result;
217+
218+
// Process preoperations
219+
int i = 0, count = PreOperations.Count;
220+
if(count > 0) Line("", LineType.VerboseDetails);
221+
foreach (var preReq in PreOperations)
222+
{
223+
i++;
224+
Line(CoreTools.Translate($"Running PreOperation ({i}/{count})..."), LineType.Information);
225+
preReq.Operation.LogLineAdded += (_, line) => Line(line.Item1, line.Item2);
226+
await preReq.Operation.MainThread();
227+
if (preReq.Operation.Status is not OperationStatus.Succeeded && preReq.MustSucceed)
228+
{
229+
Line(
230+
CoreTools.Translate($"PreOperation {i} out of {count} failed, and was tagged as necessary. Aborting..."),
231+
LineType.Error);
232+
return OperationVeredict.Failure;
233+
}
234+
Line(CoreTools.Translate($"PreOperation {i} out of {count} finished with result {preReq.Operation.Status}"), LineType.Information);
235+
Line("--------------------------------", LineType.Information);
236+
Line("", LineType.VerboseDetails);
237+
}
238+
239+
// BEGIN ACTUAL OPERATION
240+
Line(CoreTools.Translate("Starting operation..."), LineType.Information);
241+
if (Status is OperationStatus.InQueue) Status = OperationStatus.Running;
242+
243+
do
244+
{
245+
OperationStarting?.Invoke(this, EventArgs.Empty);
246+
247+
try
248+
{
249+
// Check if the operation was canceled
250+
if (Status is OperationStatus.Canceled)
251+
{
252+
result = OperationVeredict.Canceled;
253+
break;
254+
}
255+
256+
Task<OperationVeredict> op = PerformOperation();
257+
while (Status != OperationStatus.Canceled && !op.IsCompleted) await Task.Delay(100);
258+
259+
if (Status is OperationStatus.Canceled) result = OperationVeredict.Canceled;
260+
else result = op.GetAwaiter().GetResult();
261+
}
262+
catch (Exception e)
263+
{
264+
result = OperationVeredict.Failure;
265+
Logger.Error(e);
266+
foreach (string l in e.ToString().Split("\n"))
267+
{
268+
Line(l, LineType.Error);
269+
}
270+
}
271+
} while (result is OperationVeredict.AutoRetry);
272+
273+
if (result is not OperationVeredict.Success)
274+
return result;
275+
276+
// Process postoperations
277+
i = 0; count = PostOperations.Count;
278+
foreach (var postReq in PostOperations)
279+
{
280+
i++;
281+
Line("--------------------------------", LineType.Information);
282+
Line("", LineType.VerboseDetails);
283+
Line(CoreTools.Translate($"Running PostOperation ({i}/{count})..."), LineType.Information);
284+
postReq.Operation.LogLineAdded += (_, line) => Line(line.Item1, line.Item2);
285+
await postReq.Operation.MainThread();
286+
if (postReq.Operation.Status is not OperationStatus.Succeeded && postReq.MustSucceed)
287+
{
288+
Line(
289+
CoreTools.Translate($"PostOperation {i} out of {count} failed, and was tagged as necessary. Aborting..."),
290+
LineType.Error);
291+
return OperationVeredict.Failure;
292+
}
293+
Line(CoreTools.Translate($"PostOperation {i} out of {count} finished with result {postReq.Operation.Status}"), LineType.Information);
294+
}
295+
296+
return result;
297+
}
298+
355299
private bool SKIP_QUEUE;
356300

357301
public void SkipQueue()

0 commit comments

Comments
 (0)