File Coverage

blib/lib/App/NDTools/NDTool.pm
Criterion Covered Total %
statement 93 94 98.9
branch 18 20 90.0
condition 3 3 100.0
subroutine 20 21 95.2
pod 0 9 0.0
total 134 147 91.1


line stmt bran cond sub pod time code
1             package App::NDTools::NDTool;
2              
3 19     19   6793 use strict;
  19         38  
  19         642  
4 19     19   110 use warnings FATAL => 'all';
  19         37  
  19         674  
5              
6 19     19   6186 use App::NDTools::INC;
  19         66  
  19         102  
7 19     19   7689 use App::NDTools::Slurp qw(s_dump s_load);
  19         58  
  19         1299  
8 19     19   9613 use Encode::Locale;
  19         256802  
  19         933  
9 19     19   139 use Encode qw(decode);
  19         42  
  19         1389  
10 19     19   13631 use Getopt::Long qw(GetOptionsFromArray :config bundling noignore_case);
  19         199113  
  19         99  
11 19     19   4563 use Log::Log4Cli;
  19         42  
  19         1655  
12 19     19   9503 use Struct::Path 0.80 qw(path);
  19         40411  
  19         19135  
13              
14             our $VERSION = '0.34';
15              
16             sub arg_opts {
17 247     247 0 2479 my $self = shift;
18              
19             return (
20             'dump-opts' => \$self->{OPTS}->{'dump-opts'},
21             'help|h' => sub {
22 4     4   3961 $self->{OPTS}->{help} = 1;
23 4         26 die "!FINISH";
24             },
25             'ifmt=s' => \$self->{OPTS}->{ifmt},
26             'ofmt=s' => \$self->{OPTS}->{ofmt},
27             'pretty!' => \$self->{OPTS}->{pretty},
28             'verbose|v:+' => \$Log::Log4Cli::LEVEL,
29             'version|V' => sub {
30 4     4   6314 $self->{OPTS}->{version} = 1;
31 4         38 die "!FINISH";
32             },
33 247         6809 );
34             }
35              
36             sub check_args {
37 94     94 0 204 my $self = shift;
38              
39             die_fatal 'At least one argument expected', 1
40 94 100 100     296 unless (@_ or $self->{OPTS}->{'dump-opts'});
41              
42 93         281 return $self;
43             }
44              
45             sub configure {
46 131     131 0 229 my $self = shift;
47              
48 131         218 return $self->check_args(@{$self->{ARGV}});
  131         428  
49             }
50              
51             sub defaults {
52             return {
53 139     139 0 1849 'ofmt' => 'JSON',
54             'pretty' => 1,
55             'verbose' => $Log::Log4Cli::LEVEL,
56             };
57             }
58              
59             sub dump_opts {
60 1     1 0 2 my $self = shift;
61              
62 1         2 delete $self->{OPTS}->{'dump-opts'};
63 1         6 s_dump(\*STDOUT, undef, undef, $self->{OPTS});
64             }
65              
66             sub grep {
67 15     15 0 2106 my ($self, $spaths, @structs) = @_;
68 15         28 my @out;
69              
70 15         39 for my $struct (@structs) {
71 15         30 my (%map_idx, $path, $ref, $grepped);
72              
73 15         22 for (@{$spaths}) {
  15         29  
74 19         398 my @found = eval { path($struct, $_, deref => 1, paths => 1) };
  19         65  
75              
76 19         7865 while (($path, $ref) = splice @found, 0, 2) {
77             # remap array's indexes
78 34         2662 my $map_key = "";
79 34         58 my $map_path = [];
80              
81 34         47 for my $step (@{$path}) {
  34         64  
82 104 100       192 if (ref $step eq 'ARRAY') {
83 52         77 $map_key .= "[]";
84 52 100       148 unless (exists $map_idx{$map_key}->{$step->[0]}) {
85             $map_idx{$map_key}->{$step->[0]} =
86 37         52 keys %{$map_idx{$map_key}};
  37         98  
87             }
88              
89 52         74 push @{$map_path}, [$map_idx{$map_key}->{$step->[0]}];
  52         122  
90             } else { # HASH
91 52         98 $map_key .= "{$step->{K}->[0]}";
92 52         64 push @{$map_path}, $step;
  52         93  
93             }
94             }
95              
96 34         83 path($grepped, $map_path, assign => $ref, expand => 1);
97             }
98             }
99              
100 15 100       1626 push @out, $grepped if (defined $grepped);
101             }
102              
103 15         89 return @out;
104             }
105              
106             sub load_struct {
107 347     347 0 3464 my ($self, $uri, $fmt) = @_;
108              
109 347 0   0   2169 log_trace { ref $uri ? "Reading from STDIN" : "Loading '$uri'" };
  0         0  
110 347         2315 s_load($uri, $fmt);
111             }
112              
113             sub new {
114 246     246 0 17550 my $self = bless {}, shift;
115              
116 246         893 $self->{OPTS} = $self->defaults();
117             $self->{ARGV} =
118 246 100       974 [ map { decode(locale => "$_", Encode::FB_CROAK) } @_ ? @_ : @ARGV ];
  1135         42043  
119              
120 246         11505 $self->{TTY} = -t STDOUT;
121              
122 246 100       1263 unless (GetOptionsFromArray ($self->{ARGV}, $self->arg_opts)) {
123 1         583 $self->usage;
124 1         4674 die_fatal "Unsupported opts used", 1;
125             }
126              
127 245 100       305841 if ($self->{OPTS}->{help}) {
128 4         52 $self->usage;
129 4         91316 die_info, 0;
130             }
131              
132 241 100       752 if ($self->{OPTS}->{version}) {
133 4         259 print $self->VERSION . "\n";
134 4         47 die_info, 0;
135             }
136              
137 237         988 $self->configure();
138              
139 231 100       669 if ($self->{OPTS}->{'dump-opts'}) {
140 1         3 $self->dump_opts();
141 1         7 die_info, 0;
142             }
143              
144 230         869 return $self;
145             }
146              
147             sub usage {
148 7     7 0 3312 require Pod::Usage;
149 7         191358 Pod::Usage::pod2usage(
150             -exitval => 'NOEXIT',
151             -output => \*STDERR,
152             -sections => 'SYNOPSIS|OPTIONS|EXAMPLES',
153             -verbose => 99
154             );
155             }
156              
157             1; # End of App::NDTools::NDTool