runConvertCommand function

Future<int> runConvertCommand(
  1. List<String> arguments, {
  2. IOSink? out,
  3. IOSink? err,
})

flutter3d convert's main. Returns the exit code.

Implementation

Future<int> runConvertCommand(
  List<String> arguments, {
  IOSink? out,
  IOSink? err,
}) async {
  final stdoutSink = out ?? stdout;
  final stderrSink = err ?? stderr;
  final ConvertCommandSettings options;
  try {
    options = ConvertCommandSettings.parse(arguments);
  } on ConvertUsageException catch (error) {
    if (error.isHelp) {
      stdoutSink.writeln(convertUsage);
      return CliExit.ok;
    }
    stderrSink.writeln('${error.message}\n\n$convertUsage');
    return CliExit.usage;
  }

  final problems = <String>[];
  final inputs = expandInputs(options.inputs, problems);
  final reports = <ConvertReport>[
    for (final problem in problems)
      ConvertReport(problem.split(':').first, 'unknown')
        ..fail(problem.substring(problem.indexOf(':') + 2)),
  ];
  final plan = OutputPlan(options.output);
  final context = ConvertContext(
    plan: plan,
    assetPrefix: assetPrefixFor(options.output, options.assetPrefix),
    models: options.models,
    writeMaterials: options.materials,
  );

  reports.addAll(await convertInputs(inputs, context));
  if (!options.split) bundleOutputs(plan, reports);

  // Nothing is written when anything would be replaced without
  // --overwrite: a half-written run is worse than none.
  final collisions = options.overwrite ? const <String>[] : plan.collisions();
  if (collisions.isNotEmpty) {
    final owners = <String, String>{
      for (final write in plan.files) write.path: write.owner,
    };
    for (final report in reports) {
      final mine = collisions
          .where((String path) => owners[path] == report.input)
          .toList();
      if (mine.isEmpty) continue;
      report.fail(
        'would replace ${mine.join(', ')} under ${options.output}; pass '
        '--overwrite to replace them',
        as: ConvertOutcome.wouldOverwrite,
      );
    }
  }

  final changed = options.dryRun || collisions.isNotEmpty
      ? const <String>[]
      : plan.commit();

  if (options.json) {
    stdoutSink.writeln(
      const JsonEncoder.withIndent('  ').convert(
        cliJson('convert', <String, Object?>{
          'output': options.output,
          'dryRun': options.dryRun,
          'reports': <Object?>[for (final r in reports) r.toJson()],
        }),
      ),
    );
  } else {
    for (final report in reports) {
      stdoutSink.writeln(report.describe());
    }
    final planned = plan.files.length;
    stdoutSink.writeln(
      options.dryRun
          ? 'dry run: $planned files would be written under ${options.output}'
          : (collisions.isNotEmpty
                ? 'nothing written: ${collisions.length} files would be replaced'
                : '${changed.length} files written under ${options.output}'
                      '${planned - changed.length > 0 ? ', ${planned - changed.length} already up to date' : ''}'),
    );
  }
  if (options.reportPath case final String path) {
    File(path)
      ..parent.createSync(recursive: true)
      ..writeAsStringSync(
        '${const JsonEncoder.withIndent('  ').convert(convertReportDocument(reports))}\n',
      );
  }
  return exitCodeFor(reports.map((ConvertReport r) => r.outcome));
}