File Coverage

blib/lib/App/LTSVUtils.pm
Criterion Covered Total %
statement 36 62 58.0
branch 7 22 31.8
condition 0 7 0.0
subroutine 6 8 75.0
pod 3 4 75.0
total 52 103 50.4


line stmt bran cond sub pod time code
1             package App::LTSVUtils;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2019-12-19'; # DATE
5             our $DIST = 'App-LTSVUtils'; # DIST
6             our $VERSION = '0.001'; # VERSION
7              
8 1     1   87075 use 5.010001;
  1         12  
9 1     1   4 use strict;
  1         2  
  1         17  
10 1     1   4 use warnings;
  1         2  
  1         785  
11              
12             our %SPEC;
13              
14             my %args_common = (
15             );
16              
17             my %arg_filename_0 = (
18             filename => {
19             summary => 'Input LTSV file',
20             schema => 'filename*',
21             description => <<'_',
22              
23             Use `-` to read from stdin.
24              
25             _
26             req => 1,
27             pos => 0,
28             cmdline_aliases => {f=>{}},
29             },
30             );
31              
32             my %arg_filename_1 = (
33             filename => {
34             summary => 'Input LTSV file',
35             description => <<'_',
36              
37             Use `-` to read from stdin.
38              
39             _
40             schema => 'filename*',
41             req => 1,
42             pos => 1,
43             cmdline_aliases => {f=>{}},
44             },
45             );
46              
47             $SPEC{ltsvutil} = {
48             v => 1.1,
49             summary => 'Perform action on a LTSV file',
50             'x.no_index' => 1,
51             args => {
52             %args_common,
53             action => {
54             schema => ['str*', in=>[
55             'dump',
56             '2csv',
57             ]],
58             req => 1,
59             pos => 0,
60             cmdline_aliases => {a=>{}},
61             },
62             %arg_filename_1,
63             },
64             args_rels => {
65             },
66             };
67             sub ltsvutil {
68 1     1 0 3 my %args = @_;
69 1         3 my $action = $args{action};
70              
71 1         2 my $res = "";
72 1         2 my $i = 0;
73              
74 1         1 my $fh;
75 1 50       4 if ($args{filename} eq '-') {
76 0         0 $fh = *STDIN;
77             } else {
78             open $fh, "<", $args{filename} or
79 1 50       35 return [500, "Can't open input filename '$args{filename}': $!"];
80             }
81 1         15 binmode $fh, ":encoding(utf8)";
82              
83             my $code_getline = sub {
84 3     3   39 my $row0 = <$fh>;
85 3 100       22 return undef unless defined $row0;
86 2         3 chomp($row0);
87 2         3 my $row = {};
88 2         10 for my $col0 (split /\t/, $row0) {
89 4 50       15 $col0 =~ /(.+):(.*)/ or die "Row $i: Invalid column '$col0': must be in LABEL:VAL format\n";
90 4         12 $row->{$1} = $2;
91             }
92 2         6 $row;
93 1         58 };
94              
95 1         3 my $rows = [];
96 1         2 my %col_idxs;
97              
98 1         3 while (my $row = $code_getline->()) {
99 2         3 $i++;
100 2 50 0     5 if ($action eq 'dump') {
    0          
101 2         5 push @$rows, $row;
102             } elsif ($action eq '2csv' || $action eq '2tsv') {
103 0         0 push @$rows, $row;
104 0         0 for my $k (sort keys %$row) {
105 0 0       0 next if defined $col_idxs{$k};
106 0         0 $col_idxs{$k} = keys(%col_idxs);
107             }
108             } else {
109 0         0 return [400, "Unknown action '$action'"];
110             }
111             } # while getline()
112              
113 1         5 my @cols = sort { $col_idxs{$a} <=> $col_idxs{$b} } keys %col_idxs;
  0         0  
114              
115 1 50       3 if ($action eq 'dump') {
    0          
    0          
116 1         21 return [200, "OK", $rows];
117             } elsif ($action eq '2csv') {
118 0         0 require Text::CSV_XS;
119 0         0 my $csv = Text::CSV_XS->new({binary=>1});
120 0         0 $csv->print(\*STDOUT, \@cols);
121 0         0 print "\n";
122 0         0 for my $row (@$rows) {
123 0   0     0 $csv->print(\*STDOUT, [map {$row->{$_} // ''} @cols]);
  0         0  
124 0         0 print "\n";
125             }
126             } elsif ($action eq '2tsv') {
127 0 0       0 if (@cols) {
128 0         0 print join("\t", @cols) . "\n";
129 0         0 for my $row (@$rows) {
130 0   0     0 print join("\t", map { $row->{$_} // '' } @cols) . "\n";
  0         0  
131             }
132             }
133             } else {
134 0         0 return [500, "Unknown action '$action'"];
135             }
136              
137 0         0 [200, "OK", $res, {"cmdline.skip_format"=>1}];
138             } # ltsvutil
139              
140             $SPEC{ltsv_dump} = {
141             v => 1.1,
142             summary => 'Dump LTSV as data structure (array of hashes)',
143             args => {
144             %args_common,
145             %arg_filename_0,
146             },
147             };
148             sub ltsv_dump {
149 1     1 1 2397 my %args = @_;
150 1         5 ltsvutil(%args, action=>'dump');
151             }
152              
153             $SPEC{ltsv2csv} = {
154             v => 1.1,
155             summary => 'Convert LTSV to CSV',
156             args => {
157             %args_common,
158             %arg_filename_0,
159             },
160             };
161             sub ltsv2csv {
162 0     0 1   my %args = @_;
163 0           ltsvutil(%args, action=>'2csv');
164             }
165              
166             $SPEC{ltsv2tsv} = {
167             v => 1.1,
168             summary => 'Convert LTSV to TSV',
169             args => {
170             %args_common,
171             %arg_filename_0,
172             },
173             };
174             sub ltsv2tsv {
175 0     0 1   my %args = @_;
176 0           ltsvutil(%args, action=>'2tsv');
177             }
178              
179             1;
180             # ABSTRACT: CLI utilities related to LTSV
181              
182             __END__
183              
184             =pod
185              
186             =encoding UTF-8
187              
188             =head1 NAME
189              
190             App::LTSVUtils - CLI utilities related to LTSV
191              
192             =head1 VERSION
193              
194             This document describes version 0.001 of App::LTSVUtils (from Perl distribution App-LTSVUtils), released on 2019-12-19.
195              
196             =head1 DESCRIPTION
197              
198             This distribution contains the following CLI utilities:
199              
200             =over
201              
202             =item * L<dump-ltsv>
203              
204             =item * L<ltsv-dump>
205              
206             =item * L<ltsv2csv>
207              
208             =item * L<ltsv2tsv>
209              
210             =back
211              
212             =head1 FUNCTIONS
213              
214              
215             =head2 ltsv2csv
216              
217             Usage:
218              
219             ltsv2csv(%args) -> [status, msg, payload, meta]
220              
221             Convert LTSV to CSV.
222              
223             This function is not exported.
224              
225             Arguments ('*' denotes required arguments):
226              
227             =over 4
228              
229             =item * B<filename>* => I<filename>
230              
231             Input LTSV file.
232              
233             Use C<-> to read from stdin.
234              
235             =back
236              
237             Returns an enveloped result (an array).
238              
239             First element (status) is an integer containing HTTP status code
240             (200 means OK, 4xx caller error, 5xx function error). Second element
241             (msg) is a string containing error message, or 'OK' if status is
242             200. Third element (payload) is optional, the actual result. Fourth
243             element (meta) is called result metadata and is optional, a hash
244             that contains extra information.
245              
246             Return value: (any)
247              
248              
249              
250             =head2 ltsv2tsv
251              
252             Usage:
253              
254             ltsv2tsv(%args) -> [status, msg, payload, meta]
255              
256             Convert LTSV to TSV.
257              
258             This function is not exported.
259              
260             Arguments ('*' denotes required arguments):
261              
262             =over 4
263              
264             =item * B<filename>* => I<filename>
265              
266             Input LTSV file.
267              
268             Use C<-> to read from stdin.
269              
270             =back
271              
272             Returns an enveloped result (an array).
273              
274             First element (status) is an integer containing HTTP status code
275             (200 means OK, 4xx caller error, 5xx function error). Second element
276             (msg) is a string containing error message, or 'OK' if status is
277             200. Third element (payload) is optional, the actual result. Fourth
278             element (meta) is called result metadata and is optional, a hash
279             that contains extra information.
280              
281             Return value: (any)
282              
283              
284              
285             =head2 ltsv_dump
286              
287             Usage:
288              
289             ltsv_dump(%args) -> [status, msg, payload, meta]
290              
291             Dump LTSV as data structure (array of hashes).
292              
293             This function is not exported.
294              
295             Arguments ('*' denotes required arguments):
296              
297             =over 4
298              
299             =item * B<filename>* => I<filename>
300              
301             Input LTSV file.
302              
303             Use C<-> to read from stdin.
304              
305             =back
306              
307             Returns an enveloped result (an array).
308              
309             First element (status) is an integer containing HTTP status code
310             (200 means OK, 4xx caller error, 5xx function error). Second element
311             (msg) is a string containing error message, or 'OK' if status is
312             200. Third element (payload) is optional, the actual result. Fourth
313             element (meta) is called result metadata and is optional, a hash
314             that contains extra information.
315              
316             Return value: (any)
317              
318             =for Pod::Coverage ^(ltsvutil)$
319              
320             =head1 FAQ
321              
322             =head1 HOMEPAGE
323              
324             Please visit the project's homepage at L<https://metacpan.org/release/App-LTSVUtils>.
325              
326             =head1 SOURCE
327              
328             Source repository is at L<https://github.com/perlancar/perl-App-LTSVUtils>.
329              
330             =head1 BUGS
331              
332             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=App-LTSVUtils>
333              
334             When submitting a bug or request, please include a test-file or a
335             patch to an existing test-file that illustrates the bug or desired
336             feature.
337              
338             =head1 SEE ALSO
339              
340             L<https://ltsv.org>
341              
342             L<App::TSVUtils>
343              
344             L<App::CSVUtils>
345              
346             L<App::SerializeUtils>
347              
348             =head1 AUTHOR
349              
350             perlancar <perlancar@cpan.org>
351              
352             =head1 COPYRIGHT AND LICENSE
353              
354             This software is copyright (c) 2019 by perlancar@cpan.org.
355              
356             This is free software; you can redistribute it and/or modify it under
357             the same terms as the Perl 5 programming language system itself.
358              
359             =cut