File Coverage

blib/lib/YAML/yq/Helper.pm
Criterion Covered Total %
statement 14 341 4.1
branch 0 212 0.0
condition 0 8 0.0
subroutine 5 24 20.8
pod 18 19 94.7
total 37 604 6.1


line stmt bran cond sub pod time code
1             package YAML::yq::Helper;
2              
3 1     1   70754 use 5.006;
  1         6  
4 1     1   5 use strict;
  1         2  
  1         35  
5 1     1   5 use warnings;
  1         3  
  1         35  
6 1     1   468 use YAML;
  1         9586  
  1         54  
7 1     1   573 use File::Slurp qw (read_file write_file);
  1         33520  
  1         4487  
8              
9             =head1 NAME
10              
11             YAML::yq::Helper - Wrapper for yq for various common tasks so YAML files can be manipulated in a manner to preserve comments and version header.
12              
13             =head1 VERSION
14              
15             Version 0.1.1
16              
17             =cut
18              
19             our $VERSION = '0.1.1';
20              
21             =head1 SYNOPSIS
22              
23             use YAML::yq::Helper;
24              
25             my $yq = YAML::yq::Helper->new(file='/etc/suricata/suricata-ids.yaml');
26              
27             $yq->set_array(var=>'rule-files', vals=>['suricata.rules','custom.rules'])
28              
29             =head1 METHODS
30              
31             =head2 new
32              
33             Inits the object and check if a version header is present for use with the
34             ensure method.
35              
36             Will make sure the file specified exists, is a file, is readable, and is
37             writable. Otherwise it will die.
38              
39             Will also die if yq is not in the path.
40              
41             - file :: The YAML file to operate on.
42              
43             =cut
44              
45             sub new {
46 0     0 1   my ( $blank, %opts ) = @_;
47              
48 0           my $exists = `/bin/sh -c "which yq"`;
49 0 0         if ( $? != 0 ) {
50 0           die("yq not found in the path");
51             }
52              
53 0 0         if ( !defined( $opts{file} ) ) {
54 0           die('No file specified');
55             }
56              
57 0 0         if ( !-e $opts{file} ) {
58 0           die( '"' . $opts{file} . '" does not exist' );
59             }
60              
61 0 0         if ( !-f $opts{file} ) {
62 0           die( '"' . $opts{file} . '" is not a file' );
63             }
64              
65 0 0         if ( !-r $opts{file} ) {
66 0           die( '"' . $opts{file} . '" is not readable' );
67             }
68              
69             my $self = {
70             file => $opts{file},
71 0           qfile => quotemeta( $opts{file} ),
72             ensure => 0,
73             ver => undef,
74             };
75 0           bless $self;
76              
77 0           my $raw = read_file( $self->{file} );
78 0 0         if ( $raw =~ /^\%YAML\ 1\.1/ ) {
    0          
79 0           $self->{ensure} = 1;
80 0           $self->{ver} = '1.1';
81             }
82             elsif ( $raw =~ /^\%YAML\ 1\.1/ ) {
83 0           $self->{ensure} = 1;
84 0           $self->{ver} = '1.2';
85             }
86              
87 0           return $self;
88             }
89              
90             =head2 clear_array
91              
92             Clears the entries in a array, but does not delete the array.
93              
94             Will die if called on a item that is not a array.
95              
96             - var :: Variable to check. If not matching /^\./,
97             a period will be prepended.
98              
99             $yq->clear_array(var=>'rule-files');
100              
101             =cut
102              
103             sub clear_array {
104 0     0 1   my ( $self, %opts ) = @_;
105              
106 0 0         if ( !defined( $opts{var} ) ) {
    0          
107 0           die('Nothing specified for var to check');
108             }
109             elsif ( $opts{var} !~ /^\./ ) {
110 0           $opts{var} = '.' . $opts{var};
111             }
112              
113 0 0         if ( $self->is_array_clear( var => $opts{var} ) ) {
114 0           return;
115             }
116              
117 0 0         if ( $opts{var} !~ /\[\]$/ ) {
118 0           $opts{var} = $opts{var} . '[]';
119             }
120              
121 0           my $string = `yq -i "del $opts{var}" $self->{qfile}`;
122              
123 0           $self->ensure;
124             }
125              
126             =head2 clear_hash
127              
128             Clears the entries in a hash, but does not delete the hash.
129              
130             Will die if called on a item that is not a hash.
131              
132             - var :: Variable to check. If not matching /^\./,
133             a period will be prepended.
134              
135             $yq->clear_hash(var=>'rule-files');
136              
137             =cut
138              
139             sub clear_hash {
140 0     0 1   my ( $self, %opts ) = @_;
141              
142 0 0         if ( !defined( $opts{var} ) ) {
    0          
143 0           die('Nothing specified for var to check');
144             }
145             elsif ( $opts{var} !~ /^\./ ) {
146 0           $opts{var} = '.' . $opts{var};
147             }
148              
149 0 0         if ( $self->is_hash_clear( var => $opts{var} ) ) {
150 0           return;
151             }
152              
153 0 0         if ( $opts{var} !~ /\[\]$/ ) {
154 0           $opts{var} = $opts{var} . '[]';
155             }
156              
157 0           my $string = `yq -i "del $opts{var}" $self->{qfile}`;
158              
159 0           $self->ensure;
160             }
161              
162             =head2 create_array
163              
164             Creates a empty array. Unlike set_array, vals is optional.
165              
166             Will die if it already exists.
167              
168             - var :: Variable to operate on. If not matching /^\./,
169             a period will be prepended.
170              
171             - vals :: Array of values to set the array to.
172              
173             $yq->clear_array(var=>'rule-files');
174              
175             =cut
176              
177             sub create_array {
178 0     0 1   my ( $self, %opts ) = @_;
179              
180 0 0         if ( !defined( $opts{var} ) ) {
    0          
181 0           die('Nothing specified for var to check');
182             }
183             elsif ( $opts{var} !~ /^\./ ) {
184 0           $opts{var} = '.' . $opts{var};
185             }
186              
187 0           my $string;
188 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
189 0           $string = `yq -i '$opts{var}=[]' $self->{qfile}`;
190             }
191             else {
192 0           die( '"' . $opts{var} . '" already exists' );
193             }
194              
195 0 0         if ( $opts{var} !~ /\[\]$/ ) {
196 0           $opts{var} =~ s/\[\]$//;
197             }
198              
199 0           my $int = 0;
200 0           while ( defined( $opts{vals}[$int] ) ) {
201 0           my $insert = $opts{var} . '[' . $int . ']="' . $opts{vals}[$int] . '"';
202 0           $string = `yq -i '$insert' $self->{qfile}`;
203 0           $int++;
204             }
205              
206 0           $self->ensure;
207             }
208              
209             =head2 create_array
210              
211             Creates a empty array.
212              
213             Will die if it already exists.
214              
215             - var :: Variable to operate on. If not matching /^\./,
216             a period will be prepended.
217              
218             $yq->clear_array(var=>'rule-files');
219              
220             =cut
221              
222             sub create_hash {
223 0     0 0   my ( $self, %opts ) = @_;
224              
225 0 0         if ( !defined( $opts{var} ) ) {
    0          
226 0           die('Nothing specified for var to check');
227             }
228             elsif ( $opts{var} !~ /^\./ ) {
229 0           $opts{var} = '.' . $opts{var};
230             }
231              
232 0           my $string;
233 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
234 0           $string = `yq -i '$opts{var}={}' $self->{qfile}`;
235             }
236             else {
237 0           die( '"' . $opts{var} . '" already exists' );
238             }
239              
240 0           $self->ensure;
241             }
242              
243             =head2 dedup_array
244              
245             Dedup the specified array.
246              
247             Will die if called on a item that is not a array or the array
248             does not exist.
249              
250             - var :: Variable to check. If not matching /^\./,
251             a period will be prepended.
252              
253             $yq->set_array(var=>'rule-files');
254              
255             =cut
256              
257             sub dedup_array {
258 0     0 1   my ( $self, %opts ) = @_;
259              
260 0 0         if ( !defined( $opts{dedup} ) ) {
261 0           $opts{dedup} = 1;
262             }
263              
264 0 0         if ( !defined( $opts{var} ) ) {
    0          
265 0           die('Nothing specified for vals');
266             }
267             elsif ( $opts{var} !~ /^\./ ) {
268 0           $opts{var} = '.' . $opts{var};
269             }
270              
271 0 0         if ( $opts{var} =~ /\[\]$/ ) {
272 0           $opts{var} =~ s/\[\]$//;
273             }
274              
275 0           my $string;
276 0 0         if ( !$self->is_array( var => $opts{var} ) ) {
277 0           die( '"' . $opts{var} . '" is not a array or is undef' );
278             }
279              
280 0           $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
281 0           my $yaml;
282 0 0         if ( $string =~ /\[\]/ ) {
283 0           print "blank\n";
284 0           $yaml = [];
285             }
286             else {
287 0           eval { $yaml = Load($string); }
288 0 0         || eval { $string = 'foo: ' . $string; $yaml = Load($string); $yaml = $yaml->{foo} };
  0            
  0            
  0            
289             }
290              
291 0           my $int = 0;
292 0           my $existing = {};
293 0           my @new_array;
294 0           while ( defined( $yaml->[$int] ) ) {
295 0 0         if ( !defined( $existing->{ $yaml->[$int] } ) ) {
296 0           $existing->{ $yaml->[$int] } = 1;
297 0           push( @new_array, $yaml->[$int] );
298             }
299              
300 0           $int++;
301             }
302              
303 0           $self->set_array( var => $opts{var}, vals => \@new_array );
304             }
305              
306             =head2 delete
307              
308             Deletes an variable. If it is already undef, it will just return.
309              
310             - var :: Variable to check. If not matching /^\./,
311             a period will be prepended.
312              
313             $yq->delete_array(var=>'rule-files');
314              
315             =cut
316              
317             sub delete {
318 0     0 1   my ( $self, %opts ) = @_;
319              
320 0 0         if ( !defined( $opts{var} ) ) {
    0          
321 0           die('Nothing specified for var to check');
322             }
323             elsif ( $opts{var} !~ /^\./ ) {
324 0           $opts{var} = '.' . $opts{var};
325             }
326              
327 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
328 0           return;
329             }
330              
331 0           my $string = `yq -i "del $opts{var}" $self->{qfile}`;
332              
333 0           $self->ensure;
334             }
335              
336             =head2 delete_array
337              
338             Deletes an array. If it is already undef, it will just return.
339              
340             Will die if called on a item that is not a array.
341              
342             - var :: Variable to check. If not matching /^\./,
343             a period will be prepended.
344              
345             $yq->delete_array(var=>'rule-files');
346              
347             =cut
348              
349             sub delete_array {
350 0     0 1   my ( $self, %opts ) = @_;
351              
352 0 0         if ( !defined( $opts{var} ) ) {
    0          
353 0           die('Nothing specified for var to check');
354             }
355             elsif ( $opts{var} !~ /^\./ ) {
356 0           $opts{var} = '.' . $opts{var};
357             }
358              
359 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
360 0           return;
361             }
362              
363 0 0         if ( !$self->is_array( var => $opts{var} ) ) {
364 0           die( '"' . $opts{var} . '" is not a array' );
365             }
366              
367 0 0         if ( $opts{var} =~ /\[\]$/ ) {
368 0           $opts{var} =~ s/\[\]$//;
369             }
370              
371 0           my $string = `yq -i "del $opts{var}" $self->{qfile}`;
372              
373 0           $self->ensure;
374             }
375              
376             =head2 delete_hash
377              
378             Deletes an hash. If it is already undef, it will just return.
379              
380             Will die if called on a item that is not a hash.
381              
382             - var :: Variable to check. If not matching /^\./,
383             a period will be prepended.
384              
385             $yq->delete_hash(var=>'vars');
386              
387             =cut
388              
389             sub delete_hash {
390 0     0 1   my ( $self, %opts ) = @_;
391              
392 0 0         if ( !defined( $opts{var} ) ) {
    0          
393 0           die('Nothing specified for var to check');
394             }
395             elsif ( $opts{var} !~ /^\./ ) {
396 0           $opts{var} = '.' . $opts{var};
397             }
398              
399 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
400 0           return;
401             }
402              
403 0 0         if ( !$self->is_hash( var => $opts{var} ) ) {
404 0           die( '"' . $opts{var} . '" is not a hash or is undef' );
405             }
406              
407 0 0         if ( $opts{var} =~ /\[\]$/ ) {
408 0           $opts{var} =~ s/\[\]$//;
409             }
410              
411 0           my $string = `yq -i "del $opts{var}" $self->{qfile}`;
412              
413 0           $self->ensure;
414             }
415              
416             =head2 ensure
417              
418             Makes sure that the YAML file has the
419             version at the top.
420              
421             $yq->ensure;
422              
423             =cut
424              
425             sub ensure {
426 0     0 1   my ($self) = @_;
427              
428 0 0         if ( !$self->{ensure} ) {
429 0           return;
430             }
431              
432 0           my $raw = read_file( $self->{file} );
433              
434             # starts
435 0 0         if ( $raw =~ /^\%YANL/ ) {
436 0           return;
437             }
438              
439             # add dashes to the start of the raw if it is missing
440 0 0         if ( $raw !~ /^\-\-\-\n/ ) {
441 0           $raw = "---\n\n" . $raw;
442             }
443              
444             # adds the yaml version
445 0           $raw = '%YAML ' . $self->{ver} . "\n" . $raw;
446              
447 0 0         write_file( $self->{file}, $raw ) or die($@);
448              
449 0           return;
450             }
451              
452             =head2 is_array
453              
454             Checks if the specified variable in a array.
455              
456             - var :: Variable to check. If not matching /^\./,
457             a period will be prepended.
458              
459             if ( $yq->is_array(var=>'rule-files') ){
460             print "array...\n:";
461             }
462              
463             =cut
464              
465             sub is_array {
466 0     0 1   my ( $self, %opts ) = @_;
467              
468 0 0         if ( !defined( $opts{var} ) ) {
    0          
469 0           die('Nothing specified for var to check');
470             }
471             elsif ( $opts{var} !~ /^\./ ) {
472 0           $opts{var} = '.' . $opts{var};
473             }
474              
475 0           my $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
476 0 0         if ( $string =~ /\[\]/ ) {
    0          
    0          
477 0           return 1;
478             }
479             elsif ( $string =~ /\{\}/ ) {
480 0           return 0;
481             }
482             elsif ( $string eq "null\n" ) {
483 0           return 0;
484             }
485              
486 0           my $yaml;
487 0           eval { $yaml = Load($string); };
  0            
488 0 0         if ($@) {
489 0           $string = 'foo: ' . $string;
490 0   0       $yaml = Load($string) || die( $string . "\n\n" . $@ );
491 0 0         if ( ref( $yaml->{foo} ) eq 'ARRAY' ) {
492 0           return 1;
493             }
494 0           return 0;
495             }
496              
497 0 0         if ( ref($yaml) eq 'ARRAY' ) {
498 0           return 1;
499             }
500              
501 0           return 0;
502             }
503              
504             =head2 is_array_clear
505              
506             Checks if a array is clear or not.
507              
508             - var :: Variable to check. If not matching /^\./,
509             a period will be prepended.
510              
511             if ( $yq->is_array_clear(var=>'rule-files') ){
512             print "clear...\n:";
513             }
514              
515             =cut
516              
517             sub is_array_clear {
518 0     0 1   my ( $self, %opts ) = @_;
519              
520 0 0         if ( !defined( $opts{var} ) ) {
    0          
521 0           die('Nothing specified for var to check');
522             }
523             elsif ( $opts{var} !~ /^\./ ) {
524 0           $opts{var} = '.' . $opts{var};
525             }
526              
527 0 0         if ( !$self->is_array( var => $opts{var} ) ) {
528 0           die( '"' . $opts{var} . '" is not a array or is undef' );
529             }
530              
531 0           my $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
532 0 0         if ( $string =~ /\[\]/ ) {
533 0           return 1;
534             }
535              
536 0           return 0;
537             }
538              
539             =head2 is_defined
540              
541             Checks if the specified variable is defined or not.
542              
543             Will die if called on a item that is not a array.
544              
545             - var :: Variable to check. If not matching /^\./,
546             a period will be prepended.
547              
548             if ( $yq->is_defined('vars.address-groups') ){
549             print "defined...\n:";
550             }
551              
552             =cut
553              
554             sub is_defined {
555 0     0 1   my ( $self, %opts ) = @_;
556              
557 0 0         if ( !defined( $opts{var} ) ) {
    0          
558 0           die('Nothing specified for var to check');
559             }
560             elsif ( $opts{var} !~ /^\./ ) {
561 0           $opts{var} = '.' . $opts{var};
562             }
563              
564 0           my $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
565              
566 0 0         if ( $string eq "null\n" ) {
567 0           return 0;
568             }
569              
570 0           return 1;
571             }
572              
573             =head2 is_hash
574              
575             Checks if the specified variable in a hash.
576              
577             Will die if called on a item that is not a array.
578              
579             - var :: Variable to check. If not matching /^\./,
580             a period will be prepended.
581              
582             if ( $yq->is_hash('vars.address-groups') ){
583             print "hash...\n:";
584             }
585              
586             =cut
587              
588             sub is_hash {
589 0     0 1   my ( $self, %opts ) = @_;
590              
591 0 0         if ( !defined( $opts{var} ) ) {
    0          
592 0           die('Nothing specified for var to check');
593             }
594             elsif ( $opts{var} !~ /^\./ ) {
595 0           $opts{var} = '.' . $opts{var};
596             }
597              
598 0           my $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
599              
600 0 0         if ( $string =~ /\[\]/ ) {
    0          
    0          
601 0           return 0;
602             }
603             elsif ( $string =~ /\{\}/ ) {
604 0           return 1;
605             }
606             elsif ( $string eq "null\n" ) {
607 0           return 0;
608             }
609              
610 0           my $yaml = Load($string);
611              
612 0 0         if ( ref($yaml) eq 'HASH' ) {
613 0           return 1;
614             }
615              
616 0           return 0;
617             }
618              
619             =head2 is_hash_clear
620              
621             Checks if a hash is clear or not.
622              
623             Will die if called on a item that is not a hash.
624              
625             - var :: Variable to check. If not matching /^\./,
626             a period will be prepended.
627              
628             if ( ! $yq->is_hash_clear(var=>'vars') ){
629             print "not clear...\n:";
630             }
631              
632             =cut
633              
634             sub is_hash_clear {
635 0     0 1   my ( $self, %opts ) = @_;
636              
637 0 0         if ( !defined( $opts{var} ) ) {
    0          
638 0           die('Nothing specified for var to check');
639             }
640             elsif ( $opts{var} !~ /^\./ ) {
641 0           $opts{var} = '.' . $opts{var};
642             }
643              
644 0 0         if ( !$self->is_hash( var => $opts{var} ) ) {
645 0           die( '"' . $opts{var} . '" is not a hash or is undef' );
646             }
647              
648 0           my $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
649 0 0         if ( $string =~ /\{\}/ ) {
650 0           return 1;
651             }
652              
653 0           return 0;
654             }
655              
656             =head2 push_array
657              
658             Pushes the passed array onto the specified array.
659              
660             Will die if called on a item that is not a array or the array
661             does not exist.
662              
663             - var :: Variable to check. If not matching /^\./,
664             a period will be prepended.
665              
666             - vals :: Array of values to set the array to.
667              
668             $yq->set_array(var=>'rule-files',vals=>\@new_rules_files);
669              
670             =cut
671              
672             sub push_array {
673 0     0 1   my ( $self, %opts ) = @_;
674              
675 0 0         if ( !defined( $opts{vals} ) ) {
676 0           die('Nothing specified for vars');
677             }
678             else {
679 0 0         if ( !defined $opts{vals}[0] ) {
680 0           return;
681             }
682             }
683              
684 0 0         if ( !defined( $opts{dedup} ) ) {
685 0           $opts{dedup} = 1;
686             }
687              
688 0 0         if ( !defined( $opts{var} ) ) {
    0          
689 0           die('Nothing specified for vals');
690             }
691             elsif ( $opts{var} !~ /^\./ ) {
692 0           $opts{var} = '.' . $opts{var};
693             }
694              
695 0 0         if ( $opts{var} =~ /\[\]$/ ) {
696 0           $opts{var} =~ s/\[\]$//;
697             }
698              
699 0           my $string;
700 0 0         if ( !$self->is_array( var => $opts{var} ) ) {
701 0           die( '"' . $opts{var} . '" is not a array or is undef' );
702             }
703              
704 0           $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
705 0           my $yaml;
706 0 0         if ( $string =~ /\[\]/ ) {
707 0           print "blank\n";
708 0           $yaml = [];
709             }
710             else {
711 0           eval { $yaml = Load($string); }
712 0 0         || eval { $string = 'foo: ' . $string; $yaml = Load($string); $yaml = $yaml->{foo} };
  0            
  0            
  0            
713             }
714              
715 0           my @new_array;
716 0           push( @new_array, @{$yaml} );
  0            
717 0           push( @new_array, @{ $opts{vals} } );
  0            
718              
719 0           $self->set_array( var => $opts{var}, vals => \@new_array );
720             }
721              
722             =head2 set_array
723              
724             Creates an array and sets it to the values.
725              
726             If the array is already defined, it will clear it and set
727             the values to those specified.
728              
729             Will die if called on a item that is not a array.
730              
731             - var :: Variable to check. If not matching /^\./,
732             a period will be prepended.
733              
734             - vals :: Array of values to set the array to.
735              
736             $yq->set_array(var=>'rule-files',vals=>\@vals);
737              
738             =cut
739              
740             sub set_array {
741 0     0 1   my ( $self, %opts ) = @_;
742              
743 0 0         if ( !defined( $opts{vals} ) ) {
744 0           die('Nothing specified for vars');
745             }
746              
747 0 0         if ( !defined( $opts{var} ) ) {
    0          
748 0           die('Nothing specified for vals');
749             }
750             elsif ( $opts{var} !~ /^\./ ) {
751 0           $opts{var} = '.' . $opts{var};
752             }
753              
754 0           my $string;
755 0 0         if ( $self->is_defined( var => $opts{var} ) ) {
756 0           $string = `yq -i '$opts{var}=[]' $self->{qfile}`;
757             }
758             else {
759 0           $self->clear_array( var => $opts{var} );
760             }
761              
762 0 0         if ( $opts{var} !~ /\[\]$/ ) {
763 0           $opts{var} =~ s/\[\]$//;
764             }
765              
766 0           my $int = 0;
767 0           while ( defined( $opts{vals}[$int] ) ) {
768 0           my $insert = $opts{var} . '[' . $int . ']="' . $opts{vals}[$int] . '"';
769 0           $string = `yq -i '$insert' $self->{qfile}`;
770 0           $int++;
771             }
772              
773 0           $self->ensure;
774             }
775              
776             =head2 set_hash
777              
778             Creates an hash and sets it to the values.
779              
780             If the hash is already defined, it will clear it and set
781             the values to those specified.
782              
783             Will die if called on a item that is not a array.
784              
785             - var :: Variable to check. If not matching /^\./,
786             a period will be prepended.
787              
788             - hash :: A hash to use for generating the hash to be
789             added. Any undef value will be set to null.
790              
791             $yq->set_array(var=>'vars',hash=>{a=>33,bar=>undef});
792              
793             =cut
794              
795             sub set_hash {
796 0     0 1   my ( $self, %opts ) = @_;
797              
798 0           my @keys;
799 0 0         if ( !defined( $opts{hash} ) ) {
800 0           die('Nothing specified for hash');
801             }
802             else {
803 0 0         if ( ref( $opts{hash} ) ne 'HASH' ) {
804 0           die( 'The passed value for hash is a ' . ref( $opts{hash} ) . ' and not HASH' );
805             }
806              
807 0           @keys = keys( %{ $opts{hash} } );
  0            
808              
809 0           foreach my $key (@keys) {
810 0 0 0       if ( defined( $opts{hash}{$key} )
      0        
811             && ref( $opts{hash}{$key} ) ne 'SCALAR'
812             && ref( $opts{hash}{$key} ) ne '' )
813             {
814             die( 'The passed value for the key "'
815             . $key
816             . '" for the hash is a '
817 0           . ref( $opts{hash}{$key} )
818             . ' and not SCALAR or undef' );
819             }
820             }
821             }
822              
823 0 0         if ( !defined( $opts{var} ) ) {
    0          
824 0           die('Nothing specified for vals');
825             }
826             elsif ( $opts{var} !~ /^\./ ) {
827 0           $opts{var} = '.' . $opts{var};
828             }
829              
830 0 0         if ( $opts{var} =~ /\[\]$/ ) {
831 0           die( 'vars, "' . $opts{var} . '", may not contains []' );
832             }
833              
834 0 0         if ( $opts{var} !~ /\.$/ ) {
835 0           $opts{var} =~ s/\.$//;
836             }
837              
838 0           my $string;
839 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
840 0           $string = `yq -i '$opts{var}={}' $self->{qfile}`;
841             }
842             else {
843 0           $self->clear_hash( var => $opts{var} );
844             }
845              
846 0           foreach my $key (@keys) {
847 0           my $insert;
848 0 0         if ( defined( $opts{hash}{$key} ) ) {
849 0           $insert = $opts{var} . '.' . $key . '="' . $opts{hash}{$key} . '"';
850             }
851             else {
852 0           $insert = $opts{var} . '.' . $key . '=null';
853             }
854 0           $string = `yq -i '$insert' $self->{qfile}`;
855             }
856              
857 0           $self->ensure;
858             }
859              
860             =head2 set_in_array
861              
862             Ensures the values specified exist at any point in the array.
863              
864             Will create the array if it does not already exist.
865              
866             Will die if called on a item that is not a array.
867              
868             - var :: Variable to check. If not matching /^\./,
869             a period will be prepended.
870              
871             - vals :: Array of values to set the array to.
872              
873             - dedup :: If it should deduplicate the existing items
874             in the array or not.
875             Default :: 1
876              
877             $yq->set_array(var=>'rule-files',vals=>\@vals);
878              
879             =cut
880              
881             sub set_in_array {
882 0     0 1   my ( $self, %opts ) = @_;
883              
884 0           my $to_exist = {};
885 0 0         if ( !defined( $opts{vals} ) ) {
886 0           die('Nothing specified for vars');
887             }
888             else {
889 0 0         if ( !defined $opts{vals}[0] ) {
890 0           return;
891             }
892              
893 0           my $int = 0;
894 0           while ( defined( $opts{vals}[$int] ) ) {
895 0           $to_exist->{ $opts{vals}[$int] } = 1;
896 0           $int++;
897             }
898              
899             }
900              
901 0 0         if ( !defined( $opts{dedup} ) ) {
902 0           $opts{dedup} = 1;
903             }
904              
905 0 0         if ( !defined( $opts{var} ) ) {
    0          
906 0           die('Nothing specified for vals');
907             }
908             elsif ( $opts{var} !~ /^\./ ) {
909 0           $opts{var} = '.' . $opts{var};
910             }
911              
912 0 0         if ( $opts{var} =~ /\[\]$/ ) {
913 0           $opts{var} =~ s/\[\]$//;
914             }
915              
916 0           my $string;
917 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
918 0           $string = `yq -i '$opts{var}=[]' $self->{qfile}`;
919             }
920             else {
921 0 0         if ( !$self->is_array( var => $opts{var} ) ) {
922 0           die( '"' . $opts{var} . '" is not a array or is undef' );
923             }
924              
925             }
926              
927 0           $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
928 0           my $yaml;
929 0 0         if ( $string =~ /\[\]/ ) {
930 0           print "blank\n";
931 0           $yaml = [];
932             }
933             else {
934 0           eval { $yaml = Load($string); };
  0            
935             }
936              
937 0           my $int = 0;
938 0           my @exiting_a;
939 0           my $existing_h = {};
940 0           while ( defined( $yaml->[$int] ) ) {
941 0 0         if ( defined( $to_exist->{ $yaml->[$int] } ) ) {
942 0           delete( $to_exist->{ $yaml->[$int] } );
943             }
944              
945 0           push( @exiting_a, $yaml->[$int] );
946              
947 0           $existing_h->{ $yaml->[$int] } = 1;
948              
949 0           $int++;
950             }
951              
952 0           my @new_array;
953 0 0         if ( $opts{dedup} ) {
954 0           push( @new_array, keys( %{$existing_h} ) );
  0            
955 0           push( @new_array, keys( %{$to_exist} ) );
  0            
956             }
957             else {
958 0           push( @new_array, @exiting_a );
959 0           push( @new_array, keys( %{$to_exist} ) );
  0            
960             }
961              
962 0           $self->set_array( var => $opts{var}, vals => \@new_array );
963             }
964              
965             =head1 AUTHOR
966              
967             Zane C. Bowers-Hadley, C<< >>
968              
969             =head1 BUGS
970              
971             Please report any bugs or feature requests to C, or through
972             the web interface at L. I will be notified, and then you'll
973             automatically be notified of progress on your bug as I make changes.
974              
975              
976              
977              
978             =head1 SUPPORT
979              
980             You can find documentation for this module with the perldoc command.
981              
982             perldoc YAML::yq::Helper
983              
984              
985             You can also look for information at:
986              
987             =over 4
988              
989             =item * RT: CPAN's request tracker (report bugs here)
990              
991             L
992              
993             =item * CPAN Ratings
994              
995             L
996              
997             =item * Search CPAN
998              
999             L
1000              
1001             =back
1002              
1003              
1004             =head1 ACKNOWLEDGEMENTS
1005              
1006              
1007             =head1 LICENSE AND COPYRIGHT
1008              
1009             This software is Copyright (c) 2022 by Zane C. Bowers-Hadley.
1010              
1011             This is free software, licensed under:
1012              
1013             The Artistic License 2.0 (GPL Compatible)
1014              
1015              
1016             =cut
1017              
1018             1; # End of YAML::yq::Helper