File Coverage

blib/lib/CSVAWK.pm
Criterion Covered Total %
statement 30 131 22.9
branch 0 10 0.0
condition n/a
subroutine 10 19 52.6
pod 0 9 0.0
total 40 169 23.6


line stmt bran cond sub pod time code
1             package CSVAWK; # git description: 0.0.1-2-g2eeca26
2              
3 1     1   6607 use strict;
  1         2  
  1         27  
4 1     1   5 use warnings;
  1         2  
  1         23  
5              
6 1     1   410 use autodie;
  1         12025  
  1         4  
7 1     1   6398 use charnames qw(:full);
  1         26544  
  1         5  
8 1     1   600 use English qw(-no_match_vars);
  1         2792  
  1         7  
9 1     1   356 use File::Basename;
  1         2  
  1         60  
10 1     1   662 use File::Temp qw(tempdir tempfile);
  1         19129  
  1         74  
11 1     1   580 use Readonly;
  1         3553  
  1         55  
12 1     1   847 use Text::CSV_XS;
  1         8969  
  1         60  
13              
14 1     1   10 use base 'Exporter';
  1         1  
  1         91  
15             our @EXPORT_OK = qw(csvawk);
16              
17             our $VERSION = '0.1';
18              
19             Readonly my $HIDE_FS => "\N{INFORMATION SEPARATOR ONE}";
20             Readonly my $HIDE_RS => "\N{INFORMATION SEPARATOR TWO}";
21             Readonly my %SWITCHES_WITH_PARAMETERS => map { $_ => 1 } qw(
22             -f --file
23             -F --field-separator
24             -v --assign
25             -m
26             -e --source
27             -E --exec
28             -i --include
29             -l --load
30             -W
31             );
32             Readonly my $IS_PROGRAM_SWITCH => qr/^-[ef]/mxs;
33              
34             sub convert_to_identifier {
35 0     0 0   my ($str) = @_;
36 0           $str =~ s/\W+/_/mxsg;
37 0 0         if ( $str !~ m/^[[:alpha:]_]/mxs ) {
38 0           $str = "_$str";
39             }
40 0           return $str;
41             }
42              
43             sub get_csv_parser {
44 0     0 0   my $csv = Text::CSV_XS->new(
45             {
46             binary => 1,
47             auto_diag => 1,
48             eol => "\n",
49             }
50             );
51              
52 0           return $csv;
53             }
54              
55             sub hide_separators {
56 0     0 0   my ($str) = @_;
57 0           $str =~ s/,/$HIDE_FS/mxsg;
58 0           $str =~ s/\n/$HIDE_RS/mxsg;
59 0           return $str;
60             }
61              
62             sub restore_separators {
63 0     0 0   my ($str) = @_;
64 0           $str =~ s/$HIDE_FS/,/mxsg;
65 0           $str =~ s/$HIDE_RS/\n/mxsg;
66 0           return $str;
67             }
68              
69             sub split_arguments {
70 0     0 0   my (@args) = @_;
71 0           my ( @files, $has_program_switch );
72              
73 0           ARGUMENT: for my $arg ( reverse @args ) {
74 0 0         if ( $arg =~ m/^-/mxs ) {
75 0 0         if ( exists $SWITCHES_WITH_PARAMETERS{$arg} ) {
76 0           pop @files;
77             }
78 0           last ARGUMENT;
79             }
80 0           push @files, $arg;
81             }
82              
83 0           my @other_args = @args[ 0 .. $#args - $#files - 1 ];
84 0           OTHER_ARGUMENT: for my $arg (@other_args) {
85 0 0         if ( $arg =~ $IS_PROGRAM_SWITCH ) {
86 0           $has_program_switch = 1;
87 0           last OTHER_ARGUMENT;
88             }
89             }
90 0 0         if ( !$has_program_switch ) {
91 0           push @other_args, '-e', pop @files;
92             }
93 0           return \@other_args, [ reverse @files ];
94             }
95              
96             sub get_variables {
97 0     0 0   my ($files) = @_;
98              
99 0           my %results;
100 0           my $csv = get_csv_parser();
101              
102 0           for my $file ( @{$files} ) {
  0            
103 0           open my $fh, '<', $file;
104 0           my $headers = $csv->getline($fh);
105 0           $results{$file} = [ map { convert_to_identifier($_) } @{$headers} ];
  0            
  0            
106 0           close $fh;
107             }
108              
109 0           return \%results;
110             }
111              
112             sub quote_files {
113 0     0 0   my ($in_files) = @_;
114              
115 0           my %file_map;
116 0           my $dir = tempdir();
117 0           my $csv = get_csv_parser();
118              
119 0           for my $in_file ( @{$in_files} ) {
  0            
120 0           my ( $out, $out_file ) =
121             tempfile( basename($in_file) . '.XXXXXXXX', DIR => $dir );
122 0           $file_map{$in_file} = $out_file;
123 0           open my $in, '<', $in_file;
124 0           while ( my $row = $csv->getline($in) ) {
125 0           for my $field ( @{$row} ) {
  0            
126 0           $field = hide_separators($field);
127             }
128 0           $csv->print( $out, $row );
129             }
130              
131 0           close $in;
132 0           close $out;
133             }
134              
135 0           return \%file_map;
136             }
137              
138             sub build_library {
139 0     0 0   my ( $files, $file_map, $variables ) = @_;
140 0           my ( $fh, $filename ) = tempfile( SUFFIX => '.awk' );
141              
142 0           print { *{$fh} } <<'END_AWK';
  0            
  0            
143             BEGIN {
144             FS = ","
145             OFS = ","
146             }
147             FNR == 1 {
148             END_AWK
149              
150 0           for my $file ( @{$files} ) {
  0            
151 0           my $tempfile = $file_map->{$file};
152 0           print { *{$fh} } qq( if (FILENAME == "$tempfile") {\n);
  0            
  0            
153 0           my $i = 1;
154 0           for my $variable ( @{ $variables->{$file} } ) {
  0            
155 0           print { *{$fh} } " $variable = $i\n";
  0            
  0            
156 0           $i++;
157             }
158 0           print { *{$fh} } " }\n";
  0            
  0            
159             }
160 0           print { *{$fh} } "}\n";
  0            
  0            
161 0           close $fh;
162              
163 0           return $filename;
164             }
165              
166             sub csvawk {
167 0     0 0   my (@args) = @_;
168 0           my $dirname = dirname(__FILE__);
169 0           my ( $other_args, $files ) = split_arguments(@args);
170 0           my $file_map = quote_files($files);
171 0           my $variables = get_variables($files);
172 0           my $library = build_library( $files, $file_map, $variables );
173              
174             #<<<
175             my @command = (
176             'awk',
177             '-f',
178             $library,
179 0           @{$other_args},
180 0           map { $file_map->{$_} } @{$files},
  0            
  0            
181             );
182             #>>>
183              
184 0           open my $output, q(-|), @command;
185 0           while ( my $row = <$output> ) {
186 0           print restore_separators($row);
187             }
188 0           close $output;
189              
190 0           return 0;
191             }
192              
193             1;
194              
195             __END__