File Coverage

blib/lib/App/Task.pm
Criterion Covered Total %
statement 76 109 69.7
branch 18 40 45.0
condition n/a
subroutine 12 19 63.1
pod 2 2 100.0
total 108 170 63.5


line stmt bran cond sub pod time code
1             package App::Task;
2              
3 4     4   535022 use strict;
  4         38  
  4         125  
4 4     4   23 use warnings;
  4         6  
  4         172  
5              
6             our $VERSION = '0.02';
7              
8 4     4   2226 use IPC::Open3::Utils ();
  4         37615  
  4         119  
9 4     4   1829 use Text::OutputFilter;
  4         5680  
  4         134  
10 4     4   2147 use IO::Interactive::Tiny ();
  4         57  
  4         131  
11              
12             BEGIN {
13 4     4   22 no warnings "redefine";
  4         9  
  4         237  
14 4     4   1890 require Tie::Handle::Base;
15 4         5955 *Text::OutputFilter::OPEN = \&Tie::Handle::Base::OPEN;
16             }
17              
18             sub import {
19 4     4   27 no strict 'refs'; ## no critic
  4         9  
  4         5032  
20 4     4   49 *{ caller() . '::task' } = \&task;
  4         6071  
21             }
22              
23             our $depth = 0;
24             our $level = 0;
25             our $steps = {};
26             our $prev_depth = 1;
27              
28 0     0   0 sub _nl { local $depth = 0; print "\n" }
  0         0  
29              
30             sub _sys {
31 0     0   0 my @cmd = @_;
32              
33 0         0 my $rv = IPC::Open3::Utils::run_cmd(
34             @cmd,
35             {
36             autoflush => { stdout => 1, stderr => 1 },
37             carp_open3_errors => 1,
38             close_stdin => 1,
39             }
40             );
41              
42 0         0 return $rv;
43             }
44              
45             sub _escape {
46 0     0   0 my ( $str, $leave_slashes ) = @_;
47              
48 0 0       0 $str =~ s/\\/\\\\/g unless $leave_slashes;
49 0         0 $str =~ s/\n/\\n/g;
50 0         0 $str =~ s/\t/\\t/g;
51              
52 0         0 return $str;
53             }
54              
55             sub _indent {
56 135     135   5140 my ($string) = @_;
57              
58 135 50       269 warn "_indent() called outside of task()\n" if $depth < 0;
59 135 100       259 my $i = $depth <= 0 ? "" : " " x $depth;
60              
61 135         229 $string =~ s/\n/\n$i/msg;
62 135         338 return "$i$string";
63             }
64              
65             sub tie_task {
66 11     11 1 101 close ORIGOUT;
67 11         65 close ORIGERR;
68 11         222 open( *ORIGOUT, ">&", \*STDOUT );
69 11         191 open( *ORIGERR, ">&", \*STDERR );
70              
71 11         69 ORIGOUT->autoflush();
72 11         421 ORIGERR->autoflush();
73 11         316 my $o = tie( *STDOUT, "Text::OutputFilter", 0, \*ORIGOUT, \&_indent );
74 11         810 my $e = tie( *STDERR, "Text::OutputFilter", 0, \*ORIGERR, \&_indent );
75              
76 11         752 return ( $o, $e );
77             }
78              
79             sub task {
80 11     11 1 5228 my ( $msg, $code, $cmdhr ) = @_;
81 11         21 chomp($msg);
82              
83 11         20 local *STDOUT = *STDOUT;
84 11         15 local *STDERR = *STDERR;
85 11         13 local *ORIGOUT = *ORIGOUT;
86 11         27 local *ORIGERR = *ORIGERR;
87              
88 11         30 my ( $o, $e ) = tie_task();
89              
90 11         17 my $task = $code;
91 11         20 my $type = ref($code);
92 11 50       37 if ( $type eq 'ARRAY' ) {
    50          
93             my $disp = join " ", map {
94 0         0 my $copy = "$_";
95 0         0 $copy = _escape( $copy, 1 );
96 0 0       0 if ( $copy =~ m/ / ) { $copy =~ s/'/\\'/g; $copy = "'$copy'" }
  0         0  
  0         0  
97             $copy
98 0         0 } @{$code};
  0         0  
  0         0  
99 0 0       0 if ( $ENV{App_Task_DRYRUN} ) {
100 0     0   0 $task = sub { print "(DRYRUN) >_ $disp\n" };
  0         0  
101             }
102             else {
103 0 0   0   0 $task = $cmdhr->{fatal} ? sub { _sys( @{$code} ) or die "`$disp` did not exit cleanly: $?\n" } : sub { _sys( @{$code} ) };
  0 0       0  
  0         0  
  0         0  
  0         0  
104             }
105              
106             }
107             elsif ( !$type ) {
108 0         0 my $disp = _escape( $code, 0 );
109 0 0       0 if ( $ENV{App_Task_DRYRUN} ) {
110 0     0   0 $task = sub { print "(DRYRUN) >_ $disp\n" };
  0         0  
111             }
112             else {
113 0 0   0   0 $task = $cmdhr->{fatal} ? sub { _sys($code) or die "`$disp` did not exit cleanly: $?\n" } : sub { _sys($code) };
  0 0       0  
  0         0  
114             }
115             }
116              
117 11         17 my $cur_depth = $depth;
118 11         18 local $depth = $depth + 1;
119 11         19 local $level = $level + 1;
120              
121 11 100       37 $steps->{$depth} = defined $steps->{$depth} ? $steps->{$depth} + 1 : 1;
122              
123 11 100       23 if ( $prev_depth > $cur_depth ) {
124 6         8 for my $k ( keys %{$steps} ) {
  6         19  
125 13 100       40 delete $steps->{$k} if $k > $depth;
126             }
127             }
128              
129 11         17 $prev_depth = $depth;
130              
131 11 50       38 my $pre = $steps->{$depth} ? "[$level.$steps->{$depth}]" : "[$level]";
132              
133 11 50       36 my $fmt_pre = IO::Interactive::Tiny::is_interactive() ? "\e[1;107;30m" : ""; # ANSI code to highlight the heading/footing
134 11 50       143 my $fmt_pst = $fmt_pre ? "\e[0m" : "";
135              
136             {
137 11         16 local $depth = $depth - 1;
  11         16  
138 11         53 print "$fmt_pre➜➜➜➜ $pre $msg …$fmt_pst\n";
139             }
140              
141 11         322 my $ok = $task->();
142              
143             {
144 11         199 local $depth = $depth - 1;
  11         19  
145 11 100       23 if ($ok) {
146 2         10 print "$fmt_pre … $pre done ($msg).$fmt_pst\n";
147             }
148             else {
149 9         74 warn "$fmt_pre … $pre failed ($msg).$fmt_pst\n";
150             }
151             }
152              
153 11 100       251 if ( $depth < 2 ) {
154 3         13 undef $o;
155 3         86 untie *STDOUT;
156 3         86 undef $e;
157 3         71 untie *STDERR;
158             }
159              
160 11         120 return $ok;
161             }
162              
163             1;
164              
165             __END__