exec method

  1. @override
Future<Result<ShellExecResult, ExecutionError>> exec(
  1. String command, {
  2. ShellExecOptions? options,
})
override

Executes a shell command. Must never throw: all failures are encoded in the returned Result.

Implementation

@override
Future<Result<ShellExecResult, ExecutionError>> exec(
  String command, {
  ShellExecOptions? options,
}) async {
  final token = options?.cancelToken;
  if (token?.isCancelled ?? false) {
    return const Err(ExecutionError(ExecutionErrorCode.aborted, 'aborted'));
  }
  final started = await _start(command, options);
  if (started.isErr) return Err(started.errorOrNull!);
  final process = started.valueOrNull!;

  final stdout = StringBuffer();
  final stderr = StringBuffer();
  ExecutionError? callbackError;
  final stdoutDone = process.stdout
      .transform(utf8.decoder)
      .forEach(
        (chunk) => _collect(
          stdout,
          chunk,
          options?.onStdout,
          process,
          (error) => callbackError = error,
        ),
      );
  final stderrDone = process.stderr
      .transform(utf8.decoder)
      .forEach(
        (chunk) => _collect(
          stderr,
          chunk,
          options?.onStderr,
          process,
          (error) => callbackError = error,
        ),
      );

  Timer? timer;
  var timedOut = false;
  final timeout = options?.timeout;
  if (timeout != null) {
    timer = Timer(timeout, () {
      timedOut = true;
      process.kill();
    });
  }
  void onCancel(_) {
    process.kill();
  }

  token?.onCancel.then(onCancel);

  final exitCode = await process.exitCode;
  timer?.cancel();
  await Future.wait([stdoutDone, stderrDone]);

  return _result(
    callbackError: callbackError,
    timedOut: timedOut,
    timeout: timeout,
    cancelled: token?.isCancelled ?? false,
    stdout: stdout,
    stderr: stderr,
    exitCode: exitCode,
  );
}