File Coverage

blib/lib/App/Task.pm
Criterion Covered Total %
statement 80 113 70.8
branch 18 40 45.0
condition n/a
subroutine 13 20 65.0
pod 2 2 100.0
total 113 175 64.5


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