check method
Runs the stages 3 to 5 of the pipeline and missingTemplateTags for
contractCase, then, if render is on and they found no error, the
stages 7 and 8 and checkRendered.
Before the stage 7, it reports a value that the case or roleOptions
give an option that does not take it (see RoleOption.allowed), as
the command line would refuse it, and makes no choice with it.
Between the stages 7 and 8, it holds the choice of each role of the
app with a mode option to what the option states (see
RoleOption.mode). For that it makes the choices of the app a second
time, without a terminal, as smf create --no-input makes them when
the matrices generate the app. The value of a choice is what
RoleTemplate.optionsOf gives the option for it:
- with a value of the option, from the case or from roleOptions, the choice has that value in both runs, so a template that does not read the option, or not without a terminal, is an error;
- without one, the choice has the first value of the option in both runs, also when the role asks and the harness answers as a user who presses Enter;
- no role fails or asks in the run without a terminal, which gets no value for a mode option that the case leaves out;
- the two choices of the role are equal, so that the run without a terminal generates the app that the harness renders.
An app with such an error is not rendered.
Implementation
Future<ContractResult> check(ContractCase contractCase) async {
final environment = PipelineEnvironment(
_silentHost,
interactive: false,
skipExternalSetup: true,
);
final ResolverResult resolved;
try {
resolved = await resolve(
requested: contractCase.requested,
registry: registry,
environment: environment,
answers: {...contractCase.picks},
);
} on SmfUsageException catch (error) {
return ContractResult(contractCase, [SmfIssue(error.message)]);
}
final resolution = resolved.resolution;
if (resolution == null) {
return ContractResult(contractCase, resolved.issues);
}
final collection = collect(resolution, context);
final validation = validate(
registry: registry,
resolution: resolution,
collection: collection,
context: context,
interactive: environment.interactive,
skipExternalSetup: environment.skipExternalSetup,
);
final checked = ContractResult(
contractCase,
[
...resolved.issues,
...validation.issues,
...missingTemplateTags(
registry: registry,
resolution: resolution,
collection: collection,
),
..._copiedTemplateIssues(collection),
],
resolution: resolution,
collection: collection,
validation: validation,
);
if (!render || checked.errors.isNotEmpty) return checked;
final options = {...roleOptions, ...contractCase.roleOptions};
final refused = _refusedValues(options);
if (refused.isNotEmpty) return checked._with(refused);
final Map<Role, Object?> choices;
final RenderedApp app;
final answering = _EnterPrompter();
// The choices of the roles that asked, which the harness answered.
final answered = <Role, Object?>{};
final Map<String, String> answers;
try {
choices = await chooseRoles(
registry: registry,
resolution: resolution,
collection: collection,
optionValues: options,
environment: PipelineEnvironment(
_answeringHost(answering),
interactive: true,
skipExternalSetup: true,
),
context: context,
onChoice: (role, choice) {
if (!answering.asked) return;
answering.asked = false;
answered[role] = choice;
},
);
final (options: given, :problems) = await _answersOf(
answered,
resolution: resolution,
collection: collection,
options: options,
);
if (problems.isNotEmpty) {
return checked._with(problems, choices: choices);
}
final ofModes = await _modeProblems(
choices,
asked: {...answered.keys},
resolution: resolution,
collection: collection,
options: options,
answers: given,
);
if (ofModes.isNotEmpty) {
return checked._with(ofModes, choices: choices);
}
answers = given;
app = renderApp(
registry: registry,
resolution: resolution,
collection: collection,
context: context,
choices: choices,
pubspec: validation.pubspec,
);
} on SmfUsageException catch (error) {
return checked._with([SmfIssue(error.message)]);
} on GenerationFailedException catch (error) {
return checked._with([
if (error.issues.isEmpty) SmfIssue(error.message),
...error.issues,
]);
}
final rendered = checked._with(
const [],
choices: choices,
answers: answers,
app: app,
hook: hookRequest(
registry: registry,
resolution: resolution,
collection: collection,
context: context,
choices: choices,
),
);
return rendered._with(checkRendered(rendered, app));
}