line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package App::NDTools::NDQuery; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
663
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
41
|
|
4
|
1
|
|
|
1
|
|
7
|
use warnings FATAL => 'all'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
35
|
|
5
|
1
|
|
|
1
|
|
5
|
use parent 'App::NDTools::NDTool'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
24
|
|
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
52
|
use Digest::MD5 qw(md5_hex); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
49
|
|
8
|
1
|
|
|
1
|
|
6
|
use JSON qw(); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
16
|
|
9
|
1
|
|
|
1
|
|
5
|
use Log::Log4Cli; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
73
|
|
10
|
1
|
|
|
1
|
|
6
|
use App::NDTools::Slurp qw(s_dump); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
61
|
|
11
|
1
|
|
|
1
|
|
7
|
use Struct::Path 0.80 qw(list_paths path path_delta); |
|
1
|
|
|
|
|
15
|
|
|
1
|
|
|
|
|
80
|
|
12
|
1
|
|
|
1
|
|
471
|
use Struct::Path::PerlStyle 0.80 qw(str2path path2str); |
|
1
|
|
|
|
|
50529
|
|
|
1
|
|
|
|
|
72
|
|
13
|
1
|
|
|
1
|
|
9
|
use Term::ANSIColor qw(color); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1422
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our $VERSION = '0.36'; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub arg_opts { |
18
|
47
|
|
|
47
|
0
|
113
|
my $self = shift; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
return ( |
21
|
|
|
|
|
|
|
$self->SUPER::arg_opts(), |
22
|
|
|
|
|
|
|
'colors!' => \$self->{OPTS}->{colors}, |
23
|
|
|
|
|
|
|
'delete|ignore=s@' => \$self->{OPTS}->{delete}, |
24
|
|
|
|
|
|
|
'depth|d=i' => \$self->{OPTS}->{depth}, |
25
|
|
|
|
|
|
|
'grep=s@' => \$self->{OPTS}->{grep}, |
26
|
|
|
|
|
|
|
'keys' => \$self->{OPTS}->{keys}, |
27
|
|
|
|
|
|
|
'list|l' => \$self->{OPTS}->{list}, |
28
|
|
|
|
|
|
|
'md5' => \$self->{OPTS}->{md5}, |
29
|
|
|
|
|
|
|
'path|p=s' => \$self->{OPTS}->{path}, |
30
|
4
|
|
|
4
|
|
5920
|
'raw-output' => sub { $self->{OPTS}->{ofmt} = 'RAW' }, |
31
|
|
|
|
|
|
|
'replace' => \$self->{OPTS}->{replace}, |
32
|
|
|
|
|
|
|
'strict!' => \$self->{OPTS}->{strict}, |
33
|
|
|
|
|
|
|
'values|vals' => \$self->{OPTS}->{values}, |
34
|
47
|
|
|
|
|
241
|
); |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub check_args { |
38
|
45
|
|
|
45
|
0
|
84
|
my $self = shift; |
39
|
|
|
|
|
|
|
|
40
|
45
|
100
|
|
|
|
126
|
if ($self->{OPTS}->{replace}) { |
41
|
|
|
|
|
|
|
die_fatal "--replace opt can't be used with --keys", 1 |
42
|
4
|
50
|
|
|
|
23
|
if ($self->{OPTS}->{keys}); |
43
|
|
|
|
|
|
|
die_fatal "--replace opt can't be used with --list", 1 |
44
|
4
|
100
|
|
|
|
17
|
if ($self->{OPTS}->{list}); |
45
|
|
|
|
|
|
|
die_fatal "--replace opt can't be used with --md5", 1 |
46
|
3
|
100
|
|
|
|
12
|
if ($self->{OPTS}->{md5}); |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
43
|
|
|
|
|
79
|
return $self; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub configure { |
53
|
45
|
|
|
45
|
0
|
88
|
my $self = shift; |
54
|
|
|
|
|
|
|
|
55
|
45
|
|
|
|
|
196
|
$self->SUPER::configure(); |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
$self->{OPTS}->{colors} = $self->{TTY} |
58
|
43
|
100
|
|
|
|
197
|
unless (defined $self->{OPTS}->{colors}); |
59
|
|
|
|
|
|
|
|
60
|
43
|
|
|
|
|
105
|
for ( |
61
|
43
|
|
|
|
|
124
|
@{$self->{OPTS}->{grep}}, |
62
|
43
|
|
|
|
|
150
|
@{$self->{OPTS}->{delete}} |
63
|
|
|
|
|
|
|
) { |
64
|
12
|
|
|
|
|
23
|
my $tmp = eval { str2path($_) }; |
|
12
|
|
|
|
|
46
|
|
65
|
12
|
50
|
|
|
|
9642
|
die_fatal "Failed to parse '$_'", 4 if ($@); |
66
|
12
|
|
|
|
|
32
|
$_ = $tmp; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
43
|
|
|
|
|
106
|
return $self; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
sub defaults { |
73
|
47
|
|
|
47
|
0
|
107
|
my $self = shift; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
return { |
76
|
47
|
|
|
|
|
90
|
%{$self->SUPER::defaults()}, |
|
47
|
|
|
|
|
200
|
|
77
|
|
|
|
|
|
|
'color-common' => 'bold black', |
78
|
|
|
|
|
|
|
'strict' => 1, # exit with 8 if unexisted path specified |
79
|
|
|
|
|
|
|
'ofmt' => 'JSON', |
80
|
|
|
|
|
|
|
}; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub dump { |
84
|
29
|
|
|
29
|
0
|
70
|
my ($self, $uri, $data) = @_; |
85
|
|
|
|
|
|
|
|
86
|
29
|
100
|
|
|
|
95
|
$uri = \*STDOUT unless ($self->{OPTS}->{replace}); |
87
|
29
|
|
|
|
|
88
|
s_dump($uri, $self->{OPTS}->{ofmt}, {pretty => $self->{OPTS}->{pretty}}, @{$data}); |
|
29
|
|
|
|
|
112
|
|
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub exec { |
91
|
43
|
|
|
43
|
0
|
82
|
my $self = shift; |
92
|
|
|
|
|
|
|
|
93
|
43
|
50
|
|
|
|
64
|
for my $uri (@{$self->{ARGV}} ? @{$self->{ARGV}} : \*STDIN) { |
|
43
|
|
|
|
|
158
|
|
|
43
|
|
|
|
|
131
|
|
94
|
47
|
|
|
|
|
208
|
my @data = $self->load_struct($uri, $self->{OPTS}->{ifmt}); |
95
|
|
|
|
|
|
|
|
96
|
47
|
100
|
|
|
|
152
|
if (defined $self->{OPTS}->{path}) { |
97
|
15
|
|
|
|
|
29
|
my $spath = eval { str2path($self->{OPTS}->{path}) }; |
|
15
|
|
|
|
|
80
|
|
98
|
15
|
50
|
|
|
|
13748
|
die_fatal "Failed to parse '$self->{OPTS}->{path}'", 4 if ($@); |
99
|
|
|
|
|
|
|
|
100
|
15
|
100
|
|
|
|
83
|
unless (@data = path($data[0], $spath, deref => 1)) { |
101
|
|
|
|
|
|
|
die_fatal "Failed to lookup path '$self->{OPTS}->{path}'", 8 |
102
|
2
|
100
|
|
|
|
116
|
if ($self->{OPTS}->{strict}); |
103
|
1
|
|
|
|
|
3
|
next; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
@data = $self->grep($self->{OPTS}->{grep}, @data) |
108
|
45
|
100
|
|
|
|
10263
|
if (@{$self->{OPTS}->{grep}}); |
|
45
|
|
|
|
|
186
|
|
109
|
|
|
|
|
|
|
|
110
|
45
|
|
|
|
|
85
|
for my $spath (@{$self->{OPTS}->{delete}}) { |
|
45
|
|
|
|
|
134
|
|
111
|
4
|
50
|
|
|
|
127
|
map { path($_, $spath, delete => 1) if (ref $_) } @data; |
|
4
|
|
|
|
|
56
|
|
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
45
|
100
|
|
|
|
493
|
if ($self->{OPTS}->{keys}) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
115
|
4
|
|
|
|
|
16
|
$self->list_keys(\@data); |
116
|
|
|
|
|
|
|
} elsif ($self->{OPTS}->{list}) { |
117
|
7
|
|
|
|
|
38
|
$self->list($uri, \@data); |
118
|
|
|
|
|
|
|
} elsif ($self->{OPTS}->{md5}) { |
119
|
5
|
|
|
|
|
20
|
$self->md5($uri, \@data); |
120
|
|
|
|
|
|
|
} else { |
121
|
29
|
|
|
|
|
103
|
$self->dump($uri, \@data); |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
42
|
|
|
|
|
247
|
die_info "All done", 0; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
my $JSON = JSON->new->canonical->allow_nonref; |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub list_keys { |
131
|
4
|
|
|
4
|
0
|
12
|
my ($self, $data) = @_; |
132
|
4
|
|
|
|
|
6
|
my @out; |
133
|
|
|
|
|
|
|
|
134
|
4
|
|
|
|
|
9
|
for (@{$data}) { |
|
4
|
|
|
|
|
10
|
|
135
|
10
|
100
|
|
|
|
32
|
if (ref $_ eq 'HASH') { |
|
|
100
|
|
|
|
|
|
136
|
1
|
|
|
|
|
2
|
push @out, sort keys %{$_}; |
|
1
|
|
|
|
|
7
|
|
137
|
|
|
|
|
|
|
} elsif (ref $_ eq 'ARRAY') { |
138
|
3
|
|
|
|
|
5
|
push @out, "0 .. " . $#{$_}; |
|
3
|
|
|
|
|
11
|
|
139
|
|
|
|
|
|
|
} else { |
140
|
6
|
|
|
|
|
42
|
push @out, $JSON->encode($_); |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
4
|
|
|
|
|
187
|
print join("\n", @out) . "\n"; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub list { |
148
|
7
|
|
|
7
|
0
|
25
|
my ($self, $uri, $data) = @_; |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
my $base_pfx = $self->{OPTS}->{colors} |
151
|
7
|
100
|
|
|
|
43
|
? color($self->{OPTS}->{'color-common'}) : ""; |
152
|
7
|
100
|
|
|
|
80
|
my $base_sfx = $self->{OPTS}->{colors} ? color('reset') : ""; |
153
|
|
|
|
|
|
|
|
154
|
7
|
|
|
|
|
38
|
for (@{$data}) { |
|
7
|
|
|
|
|
23
|
|
155
|
7
|
|
|
|
|
44
|
my @list = list_paths($_, depth => $self->{OPTS}->{depth}); |
156
|
7
|
|
|
|
|
1409
|
my (@delta, $line, $path, $prev, $value, @out); |
157
|
|
|
|
|
|
|
|
158
|
7
|
|
|
|
|
26
|
while (@list) { |
159
|
58
|
|
|
|
|
128
|
($path, $value) = splice @list, 0, 2; |
160
|
|
|
|
|
|
|
|
161
|
58
|
|
|
|
|
154
|
@delta = path_delta($prev, $path); |
162
|
|
|
|
|
|
|
$line = $base_pfx . |
163
|
58
|
|
|
|
|
3701
|
path2str([@{$path}[0 .. @{$path} - @delta - 1]]) . $base_sfx . |
|
58
|
|
|
|
|
173
|
|
|
58
|
|
|
|
|
106
|
|
164
|
|
|
|
|
|
|
path2str(\@delta); |
165
|
|
|
|
|
|
|
|
166
|
58
|
100
|
|
|
|
6118
|
if ($self->{OPTS}->{values}) { |
167
|
37
|
|
|
|
|
72
|
$line .= " = "; |
168
|
37
|
100
|
100
|
|
|
109
|
if ($self->{OPTS}->{ofmt} eq 'RAW' and not ref ${$value}) { |
|
3
|
|
|
|
|
12
|
|
169
|
2
|
|
|
|
|
5
|
$line .= ${$value}; |
|
2
|
|
|
|
|
5
|
|
170
|
|
|
|
|
|
|
} else { |
171
|
35
|
|
|
|
|
56
|
$line .= $JSON->encode(${$value}); |
|
35
|
|
|
|
|
170
|
|
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
58
|
|
|
|
|
120
|
push @out, $line; |
176
|
58
|
|
|
|
|
171
|
$prev = $path; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
7
|
|
|
|
|
511
|
print join("\n", @out) . "\n"; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub md5 { |
184
|
5
|
|
|
5
|
0
|
11
|
my ($self, $uri, $data) = @_; |
185
|
|
|
|
|
|
|
|
186
|
5
|
50
|
|
|
|
8
|
print md5_hex($JSON->encode(@{$data} == 1 ? $data->[0] : $data)) . |
|
5
|
50
|
|
|
|
266
|
|
187
|
|
|
|
|
|
|
(ref $uri ? " -\n" : " $uri\n"); |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
1; # End of App::NDTools::NDQuery |