File Coverage

bin/yfrom
Criterion Covered Total %
statement 54 80 67.5
branch 19 28 67.8
condition n/a
subroutine 10 13 76.9
pod n/a
total 83 121 68.6


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2             package yfrom;
3             # ABSTRACT: Build YAML from another format (like JSON or CSV)
4             $yfrom::VERSION = '0.013';
5 1     1   450 use App::YAML::Filter::Base;
  1         2  
  1         18  
6 1     1   742 use Pod::Usage::Return qw( pod2usage );
  1         38340  
  1         70  
7 1     1   821 use Getopt::Long qw( GetOptionsFromArray );
  1         8835  
  1         6  
8 1     1   183 use YAML;
  1         1  
  1         57  
9 1     1   5 use Module::Runtime qw( use_module );
  1         1  
  1         7  
10              
11             $|++; # no buffering
12              
13             sub main {
14 4     4   24711 my ( $class, @argv ) = @_;
15 4         8 my %opt;
16 4         22 GetOptionsFromArray( \@argv, \%opt,
17             'help|h',
18             'version',
19             );
20 4 50       769 return pod2usage(0) if $opt{help};
21 4 50       13 if ( $opt{version} ) {
22 0         0 print "yfrom version $yfrom::VERSION (Perl $^V)\n";
23 0         0 return 0;
24             }
25              
26 4         9 my $format = shift @argv;
27 4 100       13 return pod2usage("ERROR: Must give a format") unless $format;
28 3 100       29 return pod2usage("ERROR: Unknown format '$format'")
29             unless $class->format_modules( $format );
30              
31 2 100       10 push @argv, "-" unless @argv;
32 2         6 for $ARGV ( @argv ) {
33             # We're doing a similar behavior to <>, but manually for easier testing.
34 2         3 my $fh;
35 2 100       6 if ( $ARGV eq '-' ) {
36             # Use the existing STDIN so tests can fake it
37 1         3 $fh = \*STDIN;
38             }
39             else {
40 1 50       30 unless ( open $fh, '<', $ARGV ) {
41 0         0 warn "Could not open file '$ARGV' for reading: $!\n";
42 0         0 next;
43             }
44             }
45              
46 2 50       7 my $f = $class->can_format( $format )
47             or die "Could not load format module for '$format' (tried: "
48             . ( join ", ", $class->format_modules( $format ) )
49             . ")\n";
50              
51 2         24 while ( my $line = <$fh> ) {
52 20         11131 my @docs = $f->( $line );
53 20 100       60 print YAML::Dump( @docs ) if @docs;
54             }
55             }
56              
57 2         1539 return 0;
58             }
59              
60             my %formatter;
61              
62             # Hash of "format" => [ MODULE... ]
63             # The modules are in order we should try to load them, so put the better ones first
64             my %FORMAT_MODULES = (
65             json => [qw( JSON::XS JSON::PP )],
66             csv => [qw( Text::CSV_XS Text::CSV )],
67             );
68              
69             # Hash of MODULE => formatter sub
70             sub format_sub {
71 4     4   6 my ( $class, $module ) = @_;
72              
73             # Do this to reset the state() variables every time
74             my %FORMAT_SUB = (
75             'JSON::XS' => sub {
76 20     20   35 state $json = JSON::XS->new->relaxed;
77 20         77 return $json->incr_parse( @_ );
78             },
79             'JSON::PP' => sub {
80 0     0   0 state $json = JSON::PP->new->relaxed;
81 0         0 return $json->incr_parse( @_ );
82             },
83             'Text::CSV_XS' => sub {
84 0     0   0 state $csv = Text::CSV_XS->new;
85 0         0 state @names;
86 0 0       0 if ( !@names ) {
87 0         0 $csv->parse( shift );
88 0         0 @names = $csv->fields;
89 0         0 return;
90             }
91 0         0 $csv->parse( shift );
92 0         0 my @values = $csv->fields;
93 0         0 return { map {; $names[ $_ ] => $values[ $_ ] } 0..$#values };
  0         0  
94             },
95             'Text::CSV' => sub {
96 0     0   0 state $csv = Text::CSV->new;
97 0         0 state @names;
98 0 0       0 if ( !@names ) {
99 0         0 $csv->parse( shift );
100 0         0 @names = $csv->fields;
101 0         0 return;
102             }
103 0         0 $csv->parse( shift );
104 0         0 my @values = $csv->fields;
105 0         0 return { map {; $names[ $_ ] => $values[ $_ ] } 0..$#values };
  0         0  
106             },
107 4         64 );
108              
109 4         151 return $FORMAT_SUB{ $module };
110             }
111              
112             sub format_modules {
113 8     8   16 my ( $class, $format ) = @_;
114 8 100       27 return unless $FORMAT_MODULES{ $format };
115 7         7 return @{ $FORMAT_MODULES{ $format } };
  7         29  
116             }
117              
118             sub can_format {
119 4     4   16437 my ( $class, $format ) = @_;
120 4         11 my $m = $formatter{ $format };
121 4 50       11 if ( !$m ) {
122 4         12 my @modules = $class->format_modules( $format );
123 4         9 for my $module ( @modules ) {
124 5 100       241 if ( eval { use_module( $module ); 1 } ) {
  5         18  
  3         86  
125 3         5 $m = $module;
126 3         5 last;
127             }
128             }
129             }
130 4         161 return $class->format_sub( $m );
131             }
132              
133             exit __PACKAGE__->main( @ARGV ) unless caller(0);
134              
135             __END__