File Coverage

blib/lib/IO/AsyncX/System.pm
Criterion Covered Total %
statement 46 47 97.8
branch 15 20 75.0
condition n/a
subroutine 13 14 92.8
pod 1 3 33.3
total 75 84 89.2


line stmt bran cond sub pod time code
1             package IO::AsyncX::System;
2             # ABSTRACT: system() in background for IO::Async
3 1     1   65438 use strict;
  1         3  
  1         31  
4 1     1   4 use warnings;
  1         1  
  1         23  
5              
6 1     1   412 use parent qw(IO::Async::Notifier);
  1         222  
  1         4  
7              
8             our $VERSION = '0.002';
9              
10             =head1 NAME
11              
12             IO::AsyncX::System - fork+exec, capturing STDOUT/STDERR
13              
14             =head1 VERSION
15              
16             version 0.002
17              
18             =head1 SYNOPSIS
19              
20             use feature qw(say);
21             use IO::Async::Loop;
22             use IO::AsyncX::System;
23             my $loop = IO::Async::Loop->new;
24             $loop->add(
25             my $system = IO::AsyncX::System->new
26             );
27             my ($code, $stdout, $stderr) = $system->run([qw(ls)])->get;
28             say for @$stdout;
29              
30             =head1 DESCRIPTION
31              
32             Intended as a replacement for L in L-using code.
33             Provides a single L method which will fork+exec (via L),
34             capturing STDOUT/STDERR, and returning a L holding the exit code and output.
35              
36             =cut
37              
38 1     1   9158 use curry;
  1         163  
  1         27  
39 1     1   5 use Future;
  1         1  
  1         16  
40 1     1   481 use Encode qw(decode_utf8);
  1         7543  
  1         63  
41 1     1   452 use IO::Async::Process;
  1         2391  
  1         346  
42              
43             =head1 METHODS
44              
45             =cut
46              
47             =head2 run
48              
49             Takes a single parameter defining the command to run:
50              
51             $system->run(['ls']);
52              
53             plus optional named parameters:
54              
55             =over 4
56              
57             =item * utf8 - interprets all input/output as UTF-8, so STDOUT/STDERR will be returned as arrayrefs containing Perl strings rather than raw bytes
58              
59             =item * binary - the reverse of utf8 (and the default)
60              
61             =item * stdin - an arrayref of data to pass as STDIN
62              
63             =item * timeout - kill the process if it doesn't complete within this many seconds
64              
65             =back
66              
67             Returns a L which resolves to the exit code, STDOUT and STDERR arrayrefs:
68              
69             $system->run([...]) ==> ($exitcode, $stdout_arrayref, $stderr_arrayref)
70              
71             STDIN/STDOUT/STDERR are arrayrefs split by line. In binary mode, they will hold a single element each.
72              
73             =cut
74              
75             sub run {
76 4     4 1 17916 my ($self, $cmd, %args) = @_;
77 4         12 my $stdout = [];
78 4         8 my $stderr = [];
79 4         7 my $stdin = [];
80 4 50       58 my $stdio_def = {
    100          
81             (
82             defined($stdin)
83             ? (from => join "\n", @$stdin)
84             : ()
85             ),
86             on_read => (
87             $args{utf8}
88             ? $self->curry::read_utf8($stdout)
89             : $self->curry::read_binary($stdout)
90             ),
91             };
92 4 100       86 my $stderr_def = {
93             on_read => (
94             $args{utf8}
95             ? $self->curry::read_utf8($stderr)
96             : $self->curry::read_binary($stderr)
97             ),
98             };
99 4         40 my $f = $self->loop->new_future;
100             my $proc = IO::Async::Process->new(
101             command => $cmd,
102             stdio => $stdio_def,
103             stderr => $stderr_def,
104 3 50   3   1122 on_finish => sub { $f->done($_[1], $stdout, $stderr) unless $f->is_ready },
105 0 0   0   0 on_exception => sub { $f->fail($_[1]) unless $f->is_ready },
106 4         936 );
107 4         679 $self->add_child($proc);
108 4     4   26477 $f->on_ready(sub { $self->remove_child($proc) });
  4         269  
109 4 100       174 return $f unless $args{timeout};
110             Future->wait_any(
111             $f,
112             $self->loop->timeout_future(after => $args{timeout})->on_fail(
113 1 50   1   502548 sub { $proc->kill(9) if $proc->is_running }
114             )
115 1         19 );
116             }
117              
118             sub read_binary {
119 6     6 0 4200 my ($self, $target, $stream, $buf, $eof) = @_;
120 6 100       38 push @$target, '' unless @$target;
121 6         21 $target->[0] .= $$buf;
122 6         18 $$buf = '';
123 6         16 0
124             }
125              
126             sub read_utf8 {
127 3     3 0 16987 my ($self, $target, $stream, $buf, $eof) = @_;
128 3         15 push @$target, decode_utf8($1) while $$buf =~ s/^(.*)\n//;
129 3 100       12 return 0 unless length $$buf;
130 2 100       14 push @$target, decode_utf8($$buf) if $eof;
131 2         87 0
132             }
133             1;
134              
135             __END__