=begin pod

=TITLE class Proc::Async

=SUBTITLE Running process (asynchronous interface)

    class Proc::Async { ... }

B<Note:> only the MoarVM backend of Rakudo implements C<Proc::Async> at the
moment.

C<Proc::Async> allows you to run external commands asynchronously, capturing
standard output and error handles, and optionally write to its standard input.

=begin code
    # command with arguments
    my $proc = Proc::Async.new('echo', 'foo', 'bar');

    # subscribe to new output from out and err handles:
    $proc.stdout.tap(-> $v { print "Output: $v" });
    $proc.stderr.tap(-> $v { print "Error:  $v" });

    say "Starting...";
    my $promise = $proc.start;

    # wait for the external program to terminate
    await $promise;
    say "Done.";
=end code

This produces the following output:

    Starting...
    Output: foo bar
    Done.

An example that opens an external program for writing:

    use v6;
    my $prog = Proc::Async.new(:w, 'hexdump', '-C');
    my $promise = $prog.start;
    await $prog.write(Buf.new(12, 42));
    $prog.close-stdin;
    await $promise;

=head1 Methods

=head2 method new

    method new(:$path, *@args, :$w) returns Proc::Async:D

Creates a new C<Proc::Async> object with external program name or path C<$path>
and the command line arguments C<@args>.

If C<:w> is passed to C<new>, then a pipe to the external program's standard
input stream (stdin) is opened, to which you can write with C<write> and
C<say>.

=head2 method stdout

    method stdout(Proc::Async:D: :$bin) returns Supply:D

Returns the L<Supply|/type/Supply> for the external program's standard output
stream. If C<:bin> is passed, the standard output is passed along in  binary as
L<Blob|/type/Blob>, otherwise it is interpreted as UTF-8, decoded, and passed
along as L<Str|/type/Str>.

    $proc.stdout.tap( -> $str {
        say "Got output '$str' from the external program";
    });

You must call C<stdout> before you call L<#method start>. Otherwise an
exception of class L<X::Proc::Async::TapBeforeSpawn|/type/X::Proc::Async::TapBeforeSpawn> is
thrown.

If C<stdout> is not called, the external program's standard output is not
captured at all.

Note that you cannot call C<stdout> both with and without C<:bin> on the same
object; it will throw an exception of type L<X::Proc::Async::CharsOrBytes> if
you try.

=head2 method stderr

    method stderr(Proc::Async:D: :$bin) returns Supply:D

Returns the L<Supply|/type/Supply> for the external program's standard error
stream. If C<:bin> is passed, the standard error is passed along in  binary as
L<Blob|/type/Blob>, otherwise it is interpreted as UTF-8, decoded, and passed
along as L<Str|/type/Str>.

    $proc.stderr.tap( -> $str {
        say "Got error '$str' from the external program";
    });

You must call C<stderr> before you call L<#method start>. Otherwise an
exception of class L<X::Proc::Async::TapBeforeSpawn|/type/X::Proc::Async::TapBeforeSpawn> is
thrown.

If C<stderr> is not called, the external program's standard error stream is not
captured at all.

Note that you cannot call C<stderr> both with and without C<:bin> on the same
object; it will throw an exception of type L<X::Proc::Async::CharsOrBytes> if
you try.

=head2 method w

    method w(Proc::Async:D:)

Returns a true value if C<:w> as passed to the constructor, that is, if the
external program is started with its input stream made available to input from
the program through C<print>, C<say> and C<write>.

=head2 method start

    method start(Proc::Async:D:, :$scheduler = $*SCHEDULER, :$cwd = $*CWD) returns Promise:D

Initiates spawning of the external program. Returns a promise that will be
kept with a L<Proc::Status|/type/Proc::Status> object once the external
program exits, and that will be broken if the program cannot be started.

If C<start> is called on a Proc::Async object on which it has already been
called before, an exception of L<type
X::Proc::Async::AlreadyStarted|/type/X::Proc::Async::AlreadyStarted> is
thrown.

=head2 method started

    method started(Proc::Async:D:) returns Bool:D

Returns C<False> before C<.start> has been called, and C<True> afterwards.

=head2 method path

    method path(Proc::Async:D:)

Returns the name and/or path of the external program that was passed to the
C<new> method as first argument.

=head2 method args

    method args(Proc::Async:D:) returns Positional:D

Returns the command line arguments for the external programs, as passed to the
C<new> method.

=head2 method write

    method write(Proc::Async:D: Blob:D $b, :$scheduler = $*SCHEDULER)

Write the binary data in C<$b> to the standard input stream of the external
program.

Returns a L<Promise|/type/Promise> that will be kept once the data has fully
landed in the input buffer of the external program.

The C<Proc::Async> object must be created for writing (with
C<Proc::Async.new(:w, $path, @args)>). Otherwise an
L<X::Proc::Async::OpenForWriting> exception will the thrown.

C<start> must have been called before calling method write, otherwise an
L<X::Proc::Async::MustBeStarted> exception is thrown.

=head2 method print

    method print(Proc::Async:D: Str(Any) $str, :$scheduler = $*SCHEDULER)

Write the text data in C<$str> to the standard input stream of the external
program, encoding it as UTF-8.

Returns a L<Promise|/type/Promise> that will be kept once the data has fully
landed in the input buffer of the external program.

The C<Proc::Async> object must be created for writing (with
C<Proc::Async.new(:w, $path, @args)>). Otherwise an
L<X::Proc::Async::OpenForWriting> exception will the thrown.

C<start> must have been called before calling method print, otherwise an
L<X::Proc::Async::MustBeStarted> exception is thrown.

=head2 method say

    method say(Proc::Async:D: $output, :$scheduler = $*SCHEDULER)

Calls method C<gist> on the C<$output>, adds a newline, encodes it as UTF-8,
and sends it to the standard input stream of the external
program, encoding it as UTF-8.

Returns a L<Promise|/type/Promise> that will be kept once the data has fully
landed in the input buffer of the external program.

The C<Proc::Async> object must be created for writing (with
C<Proc::Async.new(:w, $path, @args)>). Otherwise an
L<X::Proc::Async::OpenForWriting> exception will the thrown.

C<start> must have been called before calling method say, otherwise an
L<X::Proc::Async::MustBeStarted> exception is thrown.

=head2 method close-stdin

    method close-stdin(Proc::Async:D:)

Closes the standard input stream of the external program. Programs that read
from STDIN often only terminate when their input stream is closed. So if
waiting for the promise from C<#method start> hangs (for a program opened for
writing), it might be a forgotten C<close-stdin>.

The C<Proc::Async> object must be created for writing (with
C<Proc::Async.new(:w, $path, @args)>). Otherwise an
L<X::Proc::Async::OpenForWriting> exception will the thrown.

C<start> must have been called before calling method close-stdin, otherwise an
L<X::Proc::Async::MustBeStarted> exception is thrown.

=head2 method kill

    kill(Proc::Async:D: $signal = "HUP")

Sends a signal to the running program. The signal can be a signal name
("KILL" or "SIGKILL"), an integer (9) or an element of the C<Signal> enum
(Signal::SIGKILL).

=end pod
