File Coverage

blib/lib/App/PerlGlue.pm
Criterion Covered Total %
statement 90 152 59.2
branch 24 70 34.2
condition 18 51 35.2
subroutine 13 18 72.2
pod 0 1 0.0
total 145 292 49.6


line stmt bran cond sub pod time code
1             package App::PerlGlue;
2              
3 7     7   289338 use strict;
  7         15  
  7         293  
4 7     7   39 use warnings;
  7         12  
  7         377  
5 7     7   36 use feature qw(say);
  7         7  
  7         1021  
6 7     7   4891 use JSON::PP qw(encode_json decode_json);
  7         121783  
  7         630  
7 7     7   3613 use Text::ParseWords qw(parse_line);
  7         11518  
  7         15098  
8              
9             our $VERSION = '0.02';
10              
11             sub run {
12 7     7 0 673 my ($class, @argv) = @_;
13 7   50     28 my $cmd = shift @argv // 'help';
14              
15 7 50 33     62 return _help() if $cmd eq 'help' || $cmd eq '--help' || $cmd eq '-h';
      33        
16              
17 7 50 66     82 if ($cmd eq 'version' || $cmd eq '--version' || $cmd eq '-v') {
      66        
18 2         33 say "perlglue $VERSION";
19 2         7 return 0;
20             }
21              
22 5 100       20 return _cmd_upper() if $cmd eq 'upper';
23 4 100       12 return _cmd_lower() if $cmd eq 'lower';
24 3 100       11 return _cmd_lines(@argv) if $cmd eq 'lines';
25 2 50       4 return _cmd_lines(@argv) if $cmd eq 'where';
26 2 50       7 return _cmd_replace(@argv) if $cmd eq 'replace';
27 2 100       8 return _cmd_pick(@argv) if $cmd eq 'pick';
28 1 0 33     6 return _cmd_convert(@argv) if $cmd eq 'convert' || $cmd eq 'csv' || $cmd eq 'from-csv';
      33        
29 0 0       0 return _cmd_jsonl(@argv) if $cmd eq 'jsonl';
30 0 0       0 return _cmd_template(@argv) if $cmd eq 'template';
31 0 0       0 return _cmd_rename(@argv) if $cmd eq 'rename';
32              
33 0         0 warn "Unknown command: $cmd\n\n";
34 0         0 _help();
35 0         0 return 2;
36             }
37              
38 1     1   19 sub _cmd_upper { while (my $line = ) { print uc $line } return 0 }
  1         10  
  1         0  
39 1     1   18 sub _cmd_lower { while (my $line = ) { print lc $line } return 0 }
  1         22  
  1         0  
40              
41             sub _open_in {
42 3     3   14 my ($file) = @_;
43 3 100 66     23 return *STDIN unless defined $file && length $file;
44 2 50       97 open my $fh, '<', $file or die "Cannot open $file: $!";
45 2         6 return $fh;
46             }
47              
48             sub _cmd_lines {
49 1     1   2 my (@argv) = @_;
50 1         16 my ($file, $expr);
51 1         3 while (@argv) {
52 1         2 my $arg = shift @argv;
53 1 50       3 if ($arg eq '--where') { $expr = shift @argv; }
  1 0       3  
    0          
54 0         0 elsif (!defined $file) { $file = $arg; }
55 0         0 elsif (!defined $expr) { $expr = $arg; }
56             }
57 1         3 my $fh = _open_in($file);
58 1         18 while (my $line = <$fh>) {
59 2         5 local $_ = $line;
60 2 50       3 if (defined $expr) {
61 2         173 my $ok = eval $expr;
62 2 100       20 next unless $ok;
63             }
64 1         18 print $line;
65             }
66 1         0 return 0;
67             }
68              
69             sub _cmd_replace {
70 0     0   0 my (@argv) = @_;
71 0   0     0 my $expr = shift @argv // die "replace requires perl substitution expression\n";
72 0         0 my $fh = _open_in(shift @argv);
73 0         0 while (my $line = <$fh>) {
74 0         0 local $_ = $line;
75 0         0 eval $expr;
76 0         0 print $_;
77             }
78 0         0 return 0;
79             }
80              
81             sub _parse_csv_rows {
82 2     2   8 my ($fh) = @_;
83 2         4 my @rows;
84 2         71 while (my $line = <$fh>) {
85 6         517 chomp $line;
86 6 50       14 next if $line eq '';
87 6         17 push @rows, [ parse_line(',', 1, $line) ];
88             }
89 2         101 return @rows;
90             }
91              
92             sub _cmd_pick {
93 1     1   3 my (@argv) = @_;
94 1   50     3 my $file = shift @argv // die "pick requires file\n";
95 1   50     3 my $flag = shift @argv // '';
96 1 50       2 die "pick expects --csv name,email\n" unless $flag eq '--csv';
97 1   50     4 my @wanted = split /,/, (shift(@argv) // '');
98              
99 1         3 my $fh = _open_in($file);
100 1         6 my @rows = _parse_csv_rows($fh);
101 1   50     3 my $header = shift @rows // [];
102 1         2 my %idx; @idx{@$header} = (0 .. $#$header);
  1         5  
103              
104 1         10 say join ',', @wanted;
105 1         2 for my $r (@rows) {
106 2 50       4 my @out = map { defined $idx{$_} ? $r->[ $idx{$_} ] : '' } @wanted;
  4         10  
107 2         6 say join ',', @out;
108             }
109 1         0 return 0;
110             }
111              
112             sub _cmd_convert {
113 1     1   3 my (@argv) = @_;
114 1   50     2 my $file = shift @argv // die "convert requires file\n";
115 1         2 my $to;
116 1         2 while (@argv) {
117 1         2 my $a = shift @argv;
118 1 50       7 $to = shift @argv if $a eq '--to';
119             }
120 1 50 33     4 die "convert only supports --to jsonl\n" unless defined $to && $to eq 'jsonl';
121              
122 1         10 my $fh = _open_in($file);
123 1         5 my @rows = _parse_csv_rows($fh);
124 1   50     4 my $header = shift @rows // [];
125 1         2 for my $r (@rows) {
126 2         162 my %obj;
127 2         6 @obj{@$header} = @$r;
128 2         7 say encode_json(\%obj);
129             }
130 1           return 0;
131             }
132              
133             sub _cmd_jsonl {
134 0     0     my (@argv) = @_;
135 0           my ($file, $expr, $where);
136 0           while (@argv) {
137 0           my $a = shift @argv;
138 0 0 0       if ($a eq '--where') { $where = shift @argv; }
  0 0          
    0          
139 0           elsif (!defined $file && $a !~ /^\$_\-/) { $file = $a; }
140 0           elsif (!defined $expr) { $expr = $a; }
141             }
142 0 0         $expr = $where if defined $where;
143 0           my $fh = _open_in($file);
144 0           while (my $line = <$fh>) {
145 0           chomp $line;
146 0 0         next if $line eq '';
147 0           local $_ = decode_json($line);
148 0 0         if (defined $expr) {
149 0           my $ok = eval $expr;
150 0 0         next unless $ok;
151             }
152 0           say encode_json($_);
153             }
154 0           return 0;
155             }
156              
157             sub _cmd_template {
158 0     0     my (@argv) = @_;
159 0   0       my $file = shift @argv // die "template requires file\n";
160 0   0       my $tpl = shift @argv // die "template requires template string\n";
161              
162 0           my $fh = _open_in($file);
163 0           my @rows = _parse_csv_rows($fh);
164 0   0       my $header = shift @rows // [];
165 0           for my $r (@rows) {
166 0           my %obj;
167 0           @obj{@$header} = @$r;
168 0 0         (my $out = $tpl) =~ s/\{\{\s*(\w+)\s*\}\}/defined $obj{$1} ? $obj{$1} : ''/ge;
  0            
169 0           say $out;
170             }
171 0           return 0;
172             }
173              
174             sub _cmd_rename {
175 0     0     my (@argv) = @_;
176 0   0       my $expr = shift @argv // die "rename requires substitution expression\n";
177 0           for my $old (@argv) {
178 0           (my $new = $old);
179 0           local $_ = $new;
180 0           eval $expr;
181 0           $new = $_;
182 0 0         next if $new eq $old;
183 0 0         die "Target exists: $new\n" if -e $new;
184 0 0         rename $old, $new or die "rename $old -> $new failed: $!";
185 0           say "$old -> $new";
186             }
187 0           return 0;
188             }
189              
190             sub _help {
191 0     0     print <<'HELP';
192             perlglue - glue messy text into useful shapes
193              
194             Usage:
195             perlglue help
196             perlglue version
197             perlglue upper < input.txt
198             perlglue lower < input.txt
199             perlglue lines [file] [--where EXPR]
200             perlglue replace 's/foo/bar/g' [file]
201             perlglue pick users.csv --csv name,email
202             perlglue convert users.csv --to jsonl
203             perlglue jsonl logs.jsonl '$_->{status} >= 500'
204             perlglue template users.csv 'Hello, {{name}}'
205             perlglue rename 's/\s+/_/g' files...
206             HELP
207 0           return 0;
208             }
209              
210             1;
211              
212             __END__