check method

Future<ContractResult> check(
  1. ContractCase contractCase
)

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));
}