121 lines
3.9 KiB
C#
121 lines
3.9 KiB
C#
namespace AcDream.Launcher.Core.Installation;
|
|
|
|
/// <summary>
|
|
/// Strict state machine for known v1 bake events. Human output, unknown v1
|
|
/// events, and future versions are deliberately transparent; known v1 events
|
|
/// cannot be reordered, duplicated, or appended after the first terminal.
|
|
/// </summary>
|
|
internal sealed class BakeProgressProtocol
|
|
{
|
|
private BakeProgressProtocolState _state;
|
|
|
|
internal BakeStartedEvent? Started { get; private set; }
|
|
|
|
internal BakeCompletedEvent? Completed { get; private set; }
|
|
|
|
internal BakeErrorEvent? Error { get; private set; }
|
|
|
|
internal string? Violation { get; private set; }
|
|
|
|
internal bool Observe(BakeProgressEvent progressEvent)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(progressEvent);
|
|
if (Violation is not null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (progressEvent)
|
|
{
|
|
case BakeHumanOutputEvent:
|
|
case UnknownBakeProgressEvent:
|
|
case FutureBakeProgressEvent:
|
|
return true;
|
|
case MalformedBakeProgressEvent malformed:
|
|
Reject($"Malformed bake progress: {malformed.Reason}");
|
|
return false;
|
|
case BakeStartedEvent started:
|
|
if (_state != BakeProgressProtocolState.AwaitingStarted)
|
|
{
|
|
Reject(_state == BakeProgressProtocolState.Running
|
|
? "The bake protocol emitted more than one v1 started event."
|
|
: "The bake protocol emitted a known event after its terminal event.");
|
|
return false;
|
|
}
|
|
|
|
Started = started;
|
|
_state = BakeProgressProtocolState.Running;
|
|
return true;
|
|
case BakeWorkProgressEvent:
|
|
if (_state != BakeProgressProtocolState.Running)
|
|
{
|
|
Reject(KnownEventStateViolation("progress"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
case BakeCompletedEvent completed:
|
|
if (_state != BakeProgressProtocolState.Running)
|
|
{
|
|
Reject(KnownEventStateViolation("completed"));
|
|
return false;
|
|
}
|
|
|
|
Completed = completed;
|
|
_state = BakeProgressProtocolState.Completed;
|
|
return true;
|
|
case BakeErrorEvent error:
|
|
if (_state != BakeProgressProtocolState.Running)
|
|
{
|
|
Reject(KnownEventStateViolation("error"));
|
|
return false;
|
|
}
|
|
|
|
Error = error;
|
|
_state = BakeProgressProtocolState.Error;
|
|
return true;
|
|
default:
|
|
Reject("The bake protocol emitted an unsupported known event.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal void CompleteInput()
|
|
{
|
|
if (Violation is not null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_state == BakeProgressProtocolState.AwaitingStarted)
|
|
{
|
|
Reject("The bake protocol did not emit a v1 started event first.");
|
|
}
|
|
else if (_state == BakeProgressProtocolState.Running)
|
|
{
|
|
Reject("The bake protocol ended without exactly one terminal event.");
|
|
}
|
|
}
|
|
|
|
private string KnownEventStateViolation(string eventName) => _state switch
|
|
{
|
|
BakeProgressProtocolState.AwaitingStarted =>
|
|
$"The bake protocol emitted v1 {eventName} before v1 started.",
|
|
BakeProgressProtocolState.Running =>
|
|
$"The bake protocol emitted an invalid v1 {eventName} event.",
|
|
_ => "The bake protocol emitted a known event after its terminal event.",
|
|
};
|
|
|
|
private void Reject(string message)
|
|
{
|
|
Violation ??= message;
|
|
}
|
|
|
|
private enum BakeProgressProtocolState
|
|
{
|
|
AwaitingStarted,
|
|
Running,
|
|
Completed,
|
|
Error,
|
|
}
|
|
}
|