mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 15:21:31 +00:00
88662aafb1
The configuration is directly copied from test.py Bug: Change-Id: I221de5b6047289044f30fcbe270eca83c8f660d3 Reviewed-on: https://dart-review.googlesource.com/27521 Reviewed-by: Alexander Thomas <athom@google.com>
140 lines
4.9 KiB
Dart
140 lines
4.9 KiB
Dart
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'dart:io';
|
|
import 'dart:async';
|
|
|
|
/// An abstract class describing a workflow action.
|
|
abstract class WorkflowAction {}
|
|
|
|
/// [WaitForInputWorkflowAction] signals to the workflow engine that the current
|
|
/// step is waiting for user input.
|
|
class WaitForInputWorkflowAction extends WorkflowAction {}
|
|
|
|
/// [NavigateStepWorkflowAction] signals to the workflow engine that a
|
|
/// transition to a new step [nextStep] should happen. It is possible to send
|
|
/// state along by using the [payload].
|
|
class NavigateStepWorkflowAction<T> extends WorkflowAction {
|
|
WorkflowStep nextStep;
|
|
T payload;
|
|
NavigateStepWorkflowAction(this.nextStep, this.payload);
|
|
}
|
|
|
|
/// [BackWorkflowAction] signals to the workflow engine that it should pop the
|
|
/// current step and return focus to the previous step.
|
|
class BackWorkflowAction extends WorkflowAction {}
|
|
|
|
/// Base class for a single step in a larger [Workflow].
|
|
abstract class WorkflowStep<T> {
|
|
/// The [onShow] is called whenever the step receives focus from the workflow
|
|
/// engine. This happens both when transitioning and when going back. When
|
|
/// going back [payload] is null.
|
|
Future<WorkflowAction> onShow(T payload);
|
|
|
|
/// The [onLeave] is called on the current step, before the workflow engine
|
|
/// transitions to a new step. The transition can be cancelled by returning
|
|
/// true.
|
|
Future<bool> onLeave();
|
|
|
|
/// Input received by the workflow engine. The step should return what
|
|
/// workflow action to take after processing the input.
|
|
Future<WorkflowAction> input(String input);
|
|
}
|
|
|
|
/// [Workflow] is a class that makes it easy to create workflows. A workflow is
|
|
/// a collection of steps, with transitions between the steps to complete the
|
|
/// workflow. Viewed steps are kept track of by a stack, which makes it easy for
|
|
/// steps to transition back.
|
|
///
|
|
/// All access to navigation is hidden, thereby making steps unaware of the
|
|
/// workflow it is in. Navigation can only be changed by workflow actions.
|
|
class Workflow {
|
|
final List<WorkflowStep> _lastSteps = [];
|
|
|
|
WorkflowStep get currentStep {
|
|
return _lastSteps.length > 0 ? _lastSteps.last : null;
|
|
}
|
|
|
|
/// Start the workflow by providing the first step to show.
|
|
Future start(WorkflowStep firstStep) {
|
|
return _navigate(firstStep, null);
|
|
}
|
|
|
|
/// Handling the leaving of a step and asks the step if it is ok to leave.
|
|
Future _navigateLeave() async {
|
|
if (currentStep != null) {
|
|
var result = await currentStep.onLeave();
|
|
if (result == true) {
|
|
// Cancel the navigation. We should wait for input.
|
|
return _handleWorkflowAction(new WaitForInputWorkflowAction());
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Handling the navigation to a step and adding the new step as the current.
|
|
Future _navigate<T>(WorkflowStep navigateTo, T payload) async {
|
|
await _navigateLeave();
|
|
_lastSteps.add(navigateTo);
|
|
await _handleWorkflowAction(await currentStep.onShow(payload));
|
|
}
|
|
|
|
/// Handling the workflow actions a feedback recursive loop.
|
|
Future _handleWorkflowAction(WorkflowAction action) async {
|
|
if (action is WaitForInputWorkflowAction) {
|
|
String input = stdin.readLineSync();
|
|
return _handleWorkflowAction(await currentStep.input(input));
|
|
} else if (action is NavigateStepWorkflowAction) {
|
|
return _navigate(action.nextStep, action.payload);
|
|
} else if (action is BackWorkflowAction) {
|
|
await _navigateLeave();
|
|
_lastSteps.removeLast();
|
|
var lastStep = _lastSteps.last;
|
|
while (_lastSteps.isNotEmpty && lastStep is ComputeStep) {
|
|
lastStep = _lastSteps.removeLast();
|
|
}
|
|
if (lastStep != null) {
|
|
return _handleWorkflowAction(await lastStep.onShow(null));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// [ComputeStepPayload] is the payload for doing [ComputeStep].
|
|
class ComputeStepPayload {
|
|
final Future action;
|
|
final String message;
|
|
final WorkflowStep completedStep;
|
|
ComputeStepPayload(this.action, this.message, this.completedStep);
|
|
}
|
|
|
|
/// [ComputeStep] is similar to a loading screen, showing a dot every second
|
|
/// until the computation in the future [action] in the payload is completed.
|
|
/// When completed, it will ask for a transition by returning
|
|
/// [NavigateStepWorkflowAction].
|
|
class ComputeStep extends WorkflowStep<ComputeStepPayload> {
|
|
@override
|
|
Future<bool> onLeave() async {
|
|
// Do nothing.
|
|
return false;
|
|
}
|
|
|
|
@override
|
|
Future<WorkflowAction> onShow(ComputeStepPayload payload) async {
|
|
stdout.write(payload.message);
|
|
var timer = new Timer(new Duration(seconds: 1), () {
|
|
stdout.write(".");
|
|
});
|
|
var result = await payload.action;
|
|
timer.cancel();
|
|
stdout.write("\n");
|
|
return new NavigateStepWorkflowAction(payload.completedStep, result);
|
|
}
|
|
|
|
@override
|
|
Future<WorkflowAction> input(String input) {
|
|
// Do nothing.
|
|
return null;
|
|
}
|
|
}
|