File Coverage

blib/lib/YAML/yq/Helper.pm
Criterion Covered Total %
statement 14 331 4.2
branch 0 206 0.0
condition 0 6 0.0
subroutine 5 24 20.8
pod 18 19 94.7
total 37 586 6.3


line stmt bran cond sub pod time code
1             package YAML::yq::Helper;
2              
3 1     1   69751 use 5.006;
  1         3  
4 1     1   6 use strict;
  1         2  
  1         39  
5 1     1   6 use warnings;
  1         2  
  1         38  
6 1     1   472 use YAML;
  1         9593  
  1         64  
7 1     1   639 use File::Slurp qw (read_file write_file);
  1         33768  
  1         4757  
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.0
16              
17             =cut
18              
19             our $VERSION = '0.1.0';
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); };
  0            
288             }
289              
290 0           my $int = 0;
291 0           my $existing = {};
292 0           my @new_array;
293 0           while ( defined( $yaml->[$int] ) ) {
294 0 0         if ( !defined( $existing->{ $yaml->[$int] } ) ) {
295 0           $existing->{ $yaml->[$int] } = 1;
296 0           push( @new_array, $yaml->[$int] );
297             }
298              
299 0           $int++;
300             }
301              
302 0           $self->set_array( var => $opts{var}, vals => \@new_array );
303             }
304              
305             =head2 delete
306              
307             Deletes an variable. If it is already undef, it will just return.
308              
309             - var :: Variable to check. If not matching /^\./,
310             a period will be prepended.
311              
312             $yq->delete_array(var=>'rule-files');
313              
314             =cut
315              
316             sub delete {
317 0     0 1   my ( $self, %opts ) = @_;
318              
319 0 0         if ( !defined( $opts{var} ) ) {
    0          
320 0           die('Nothing specified for var to check');
321             }
322             elsif ( $opts{var} !~ /^\./ ) {
323 0           $opts{var} = '.' . $opts{var};
324             }
325              
326 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
327 0           return;
328             }
329              
330 0           my $string = `yq -i "del $opts{var}" $self->{qfile}`;
331              
332 0           $self->ensure;
333             }
334              
335             =head2 delete_array
336              
337             Deletes an array. If it is already undef, it will just return.
338              
339             Will die if called on a item that is not a array.
340              
341             - var :: Variable to check. If not matching /^\./,
342             a period will be prepended.
343              
344             $yq->delete_array(var=>'rule-files');
345              
346             =cut
347              
348             sub delete_array {
349 0     0 1   my ( $self, %opts ) = @_;
350              
351 0 0         if ( !defined( $opts{var} ) ) {
    0          
352 0           die('Nothing specified for var to check');
353             }
354             elsif ( $opts{var} !~ /^\./ ) {
355 0           $opts{var} = '.' . $opts{var};
356             }
357              
358 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
359 0           return;
360             }
361              
362 0 0         if ( !$self->is_array( var => $opts{var} ) ) {
363 0           die( '"' . $opts{var} . '" is not a array' );
364             }
365              
366 0 0         if ( $opts{var} =~ /\[\]$/ ) {
367 0           $opts{var} =~ s/\[\]$//;
368             }
369              
370 0           my $string = `yq -i "del $opts{var}" $self->{qfile}`;
371              
372 0           $self->ensure;
373             }
374              
375             =head2 delete_hash
376              
377             Deletes an hash. If it is already undef, it will just return.
378              
379             Will die if called on a item that is not a hash.
380              
381             - var :: Variable to check. If not matching /^\./,
382             a period will be prepended.
383              
384             $yq->delete_hash(var=>'vars');
385              
386             =cut
387              
388             sub delete_hash {
389 0     0 1   my ( $self, %opts ) = @_;
390              
391 0 0         if ( !defined( $opts{var} ) ) {
    0          
392 0           die('Nothing specified for var to check');
393             }
394             elsif ( $opts{var} !~ /^\./ ) {
395 0           $opts{var} = '.' . $opts{var};
396             }
397              
398 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
399 0           return;
400             }
401              
402 0 0         if ( !$self->is_hash( var => $opts{var} ) ) {
403 0           die( '"' . $opts{var} . '" is not a hash or is undef' );
404             }
405              
406 0 0         if ( $opts{var} =~ /\[\]$/ ) {
407 0           $opts{var} =~ s/\[\]$//;
408             }
409              
410 0           my $string = `yq -i "del $opts{var}" $self->{qfile}`;
411              
412 0           $self->ensure;
413             }
414              
415             =head2 ensure
416              
417             Makes sure that the YAML file has the
418             version at the top.
419              
420             $yq->ensure;
421              
422             =cut
423              
424             sub ensure {
425 0     0 1   my ($self) = @_;
426              
427 0 0         if ( !$self->{ensure} ) {
428 0           return;
429             }
430              
431 0           my $raw = read_file( $self->{file} );
432              
433             # starts
434 0 0         if ( $raw =~ /^\%YANL/ ) {
435 0           return;
436             }
437              
438             # add dashes to the start of the raw if it is missing
439 0 0         if ( $raw !~ /^\-\-\-\n/ ) {
440 0           $raw = "---\n\n" . $raw;
441             }
442              
443             # adds the yaml version
444 0           $raw = '%YAML ' . $self->{ver} . "\n" . $raw;
445              
446 0 0         write_file( $self->{file}, $raw ) or die($@);
447              
448 0           return;
449             }
450              
451             =head2 is_array
452              
453             Checks if the specified variable in a array.
454              
455             - var :: Variable to check. If not matching /^\./,
456             a period will be prepended.
457              
458             if ( $yq->is_array(var=>'rule-files') ){
459             print "array...\n:";
460             }
461              
462             =cut
463              
464             sub is_array {
465 0     0 1   my ( $self, %opts ) = @_;
466              
467 0 0         if ( !defined( $opts{var} ) ) {
    0          
468 0           die('Nothing specified for var to check');
469             }
470             elsif ( $opts{var} !~ /^\./ ) {
471 0           $opts{var} = '.' . $opts{var};
472             }
473              
474 0           my $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
475 0 0         if ( $string =~ /\[\]/ ) {
    0          
    0          
476 0           return 1;
477             }
478             elsif ( $string =~ /\{\}/ ) {
479 0           return 0;
480             }
481             elsif ( $string eq "null\n" ) {
482 0           return 0;
483             }
484              
485 0           my $yaml;
486 0           eval { $yaml = Load($string); };
  0            
487 0 0         if ($@) {
488 0           die($@);
489             }
490              
491 0 0         if ( ref($yaml) eq 'ARRAY' ) {
492 0           return 1;
493             }
494              
495 0           return 0;
496             }
497              
498             =head2 is_array_clear
499              
500             Checks if a array is clear or not.
501              
502             - var :: Variable to check. If not matching /^\./,
503             a period will be prepended.
504              
505             if ( $yq->is_array_clear(var=>'rule-files') ){
506             print "clear...\n:";
507             }
508              
509             =cut
510              
511             sub is_array_clear {
512 0     0 1   my ( $self, %opts ) = @_;
513              
514 0 0         if ( !defined( $opts{var} ) ) {
    0          
515 0           die('Nothing specified for var to check');
516             }
517             elsif ( $opts{var} !~ /^\./ ) {
518 0           $opts{var} = '.' . $opts{var};
519             }
520              
521 0 0         if ( !$self->is_array( var => $opts{var} ) ) {
522 0           die( '"' . $opts{var} . '" is not a array or is undef' );
523             }
524              
525 0           my $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
526 0 0         if ( $string =~ /\[\]/ ) {
527 0           return 1;
528             }
529              
530 0           return 0;
531             }
532              
533             =head2 is_defined
534              
535             Checks if the specified variable is defined or not.
536              
537             Will die if called on a item that is not a array.
538              
539             - var :: Variable to check. If not matching /^\./,
540             a period will be prepended.
541              
542             if ( $yq->is_defined('vars.address-groups') ){
543             print "defined...\n:";
544             }
545              
546             =cut
547              
548             sub is_defined {
549 0     0 1   my ( $self, %opts ) = @_;
550              
551 0 0         if ( !defined( $opts{var} ) ) {
    0          
552 0           die('Nothing specified for var to check');
553             }
554             elsif ( $opts{var} !~ /^\./ ) {
555 0           $opts{var} = '.' . $opts{var};
556             }
557              
558 0           my $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
559              
560 0 0         if ( $string eq "null\n" ) {
561 0           return 0;
562             }
563              
564 0           return 1;
565             }
566              
567             =head2 is_hash
568              
569             Checks if the specified variable in a hash.
570              
571             Will die if called on a item that is not a array.
572              
573             - var :: Variable to check. If not matching /^\./,
574             a period will be prepended.
575              
576             if ( $yq->is_hash('vars.address-groups') ){
577             print "hash...\n:";
578             }
579              
580             =cut
581              
582             sub is_hash {
583 0     0 1   my ( $self, %opts ) = @_;
584              
585 0 0         if ( !defined( $opts{var} ) ) {
    0          
586 0           die('Nothing specified for var to check');
587             }
588             elsif ( $opts{var} !~ /^\./ ) {
589 0           $opts{var} = '.' . $opts{var};
590             }
591              
592 0           my $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
593              
594 0 0         if ( $string =~ /\[\]/ ) {
    0          
    0          
595 0           return 0;
596             }
597             elsif ( $string =~ /\{\}/ ) {
598 0           return 1;
599             }
600             elsif ( $string eq "null\n" ) {
601 0           return 0;
602             }
603              
604 0           my $yaml = Load($string);
605              
606 0 0         if ( ref($yaml) eq 'HASH' ) {
607 0           return 1;
608             }
609              
610 0           return 0;
611             }
612              
613             =head2 is_hash_clear
614              
615             Checks if a hash is clear or not.
616              
617             Will die if called on a item that is not a hash.
618              
619             - var :: Variable to check. If not matching /^\./,
620             a period will be prepended.
621              
622             if ( ! $yq->is_hash_clear(var=>'vars') ){
623             print "not clear...\n:";
624             }
625              
626             =cut
627              
628             sub is_hash_clear {
629 0     0 1   my ( $self, %opts ) = @_;
630              
631 0 0         if ( !defined( $opts{var} ) ) {
    0          
632 0           die('Nothing specified for var to check');
633             }
634             elsif ( $opts{var} !~ /^\./ ) {
635 0           $opts{var} = '.' . $opts{var};
636             }
637              
638 0 0         if ( !$self->is_hash( var => $opts{var} ) ) {
639 0           die( '"' . $opts{var} . '" is not a hash or is undef' );
640             }
641              
642 0           my $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
643 0 0         if ( $string =~ /\{\}/ ) {
644 0           return 1;
645             }
646              
647 0           return 0;
648             }
649              
650             =head2 push_array
651              
652             Pushes the passed array onto the specified array.
653              
654             Will die if called on a item that is not a array or the array
655             does not exist.
656              
657             - var :: Variable to check. If not matching /^\./,
658             a period will be prepended.
659              
660             - vals :: Array of values to set the array to.
661              
662             $yq->set_array(var=>'rule-files',vals=>\@new_rules_files);
663              
664             =cut
665              
666             sub push_array {
667 0     0 1   my ( $self, %opts ) = @_;
668              
669 0 0         if ( !defined( $opts{vals} ) ) {
670 0           die('Nothing specified for vars');
671             }
672             else {
673 0 0         if ( !defined $opts{vals}[0] ) {
674 0           return;
675             }
676             }
677              
678 0 0         if ( !defined( $opts{dedup} ) ) {
679 0           $opts{dedup} = 1;
680             }
681              
682 0 0         if ( !defined( $opts{var} ) ) {
    0          
683 0           die('Nothing specified for vals');
684             }
685             elsif ( $opts{var} !~ /^\./ ) {
686 0           $opts{var} = '.' . $opts{var};
687             }
688              
689 0 0         if ( $opts{var} =~ /\[\]$/ ) {
690 0           $opts{var} =~ s/\[\]$//;
691             }
692              
693 0           my $string;
694 0 0         if ( !$self->is_array( var => $opts{var} ) ) {
695 0           die( '"' . $opts{var} . '" is not a array or is undef' );
696             }
697              
698 0           $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
699 0           my $yaml;
700 0 0         if ( $string =~ /\[\]/ ) {
701 0           print "blank\n";
702 0           $yaml = [];
703             }
704             else {
705 0           eval { $yaml = Load($string); };
  0            
706             }
707              
708 0           my @new_array;
709 0           push( @new_array, @{$yaml} );
  0            
710 0           push( @new_array, @{ $opts{vals} } );
  0            
711              
712 0           $self->set_array( var => $opts{var}, vals => \@new_array );
713             }
714              
715             =head2 set_array
716              
717             Creates an array and sets it to the values.
718              
719             If the array is already defined, it will clear it and set
720             the values to those specified.
721              
722             Will die if called on a item that is not a array.
723              
724             - var :: Variable to check. If not matching /^\./,
725             a period will be prepended.
726              
727             - vals :: Array of values to set the array to.
728              
729             $yq->set_array(var=>'rule-files',vals=>\@vals);
730              
731             =cut
732              
733             sub set_array {
734 0     0 1   my ( $self, %opts ) = @_;
735              
736 0 0         if ( !defined( $opts{vals} ) ) {
737 0           die('Nothing specified for vars');
738             }
739              
740 0 0         if ( !defined( $opts{var} ) ) {
    0          
741 0           die('Nothing specified for vals');
742             }
743             elsif ( $opts{var} !~ /^\./ ) {
744 0           $opts{var} = '.' . $opts{var};
745             }
746              
747 0           my $string;
748 0 0         if ( $self->is_defined( var => $opts{var} ) ) {
749 0           $string = `yq -i '$opts{var}=[]' $self->{qfile}`;
750             }
751             else {
752 0           $self->clear_array( var => $opts{var} );
753             }
754              
755 0 0         if ( $opts{var} !~ /\[\]$/ ) {
756 0           $opts{var} =~ s/\[\]$//;
757             }
758              
759 0           my $int = 0;
760 0           while ( defined( $opts{vals}[$int] ) ) {
761 0           my $insert = $opts{var} . '[' . $int . ']="' . $opts{vals}[$int] . '"';
762 0           $string = `yq -i '$insert' $self->{qfile}`;
763 0           $int++;
764             }
765              
766 0           $self->ensure;
767             }
768              
769             =head2 set_hash
770              
771             Creates an hash and sets it to the values.
772              
773             If the hash is already defined, it will clear it and set
774             the values to those specified.
775              
776             Will die if called on a item that is not a array.
777              
778             - var :: Variable to check. If not matching /^\./,
779             a period will be prepended.
780              
781             - hash :: A hash to use for generating the hash to be
782             added. Any undef value will be set to null.
783              
784             $yq->set_array(var=>'vars',hash=>{a=>33,bar=>undef});
785              
786             =cut
787              
788             sub set_hash {
789 0     0 1   my ( $self, %opts ) = @_;
790              
791 0           my @keys;
792 0 0         if ( !defined( $opts{hash} ) ) {
793 0           die('Nothing specified for hash');
794             }
795             else {
796 0 0         if ( ref( $opts{hash} ) ne 'HASH' ) {
797 0           die( 'The passed value for hash is a ' . ref( $opts{hash} ) . ' and not HASH' );
798             }
799              
800 0           @keys = keys( %{ $opts{hash} } );
  0            
801              
802 0           foreach my $key (@keys) {
803 0 0 0       if ( defined( $opts{hash}{$key} )
      0        
804             && ref( $opts{hash}{$key} ) ne 'SCALAR'
805             && ref( $opts{hash}{$key} ) ne '' )
806             {
807             die( 'The passed value for the key "'
808             . $key
809             . '" for the hash is a '
810 0           . ref( $opts{hash}{$key} )
811             . ' and not SCALAR or undef' );
812             }
813             }
814             }
815              
816 0 0         if ( !defined( $opts{var} ) ) {
    0          
817 0           die('Nothing specified for vals');
818             }
819             elsif ( $opts{var} !~ /^\./ ) {
820 0           $opts{var} = '.' . $opts{var};
821             }
822              
823 0 0         if ( $opts{var} =~ /\[\]$/ ) {
824 0           die( 'vars, "' . $opts{var} . '", may not contains []' );
825             }
826              
827 0 0         if ( $opts{var} !~ /\.$/ ) {
828 0           $opts{var} =~ s/\.$//;
829             }
830              
831 0           my $string;
832 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
833 0           $string = `yq -i '$opts{var}={}' $self->{qfile}`;
834             }
835             else {
836 0           $self->clear_hash( var => $opts{var} );
837             }
838              
839 0           foreach my $key (@keys) {
840 0           my $insert;
841 0 0         if ( defined( $opts{hash}{$key} ) ) {
842 0           $insert = $opts{var} . '.' . $key . '="' . $opts{hash}{$key} . '"';
843             }
844             else {
845 0           $insert = $opts{var} . '.' . $key . '=null';
846             }
847 0           $string = `yq -i '$insert' $self->{qfile}`;
848             }
849              
850 0           $self->ensure;
851             }
852              
853             =head2 set_in_array
854              
855             Ensures the values specified exist at any point in the array.
856              
857             Will create the array if it does not already exist.
858              
859             Will die if called on a item that is not a array.
860              
861             - var :: Variable to check. If not matching /^\./,
862             a period will be prepended.
863              
864             - vals :: Array of values to set the array to.
865              
866             - dedup :: If it should deduplicate the existing items
867             in the array or not.
868             Default :: 1
869              
870             $yq->set_array(var=>'rule-files',vals=>\@vals);
871              
872             =cut
873              
874             sub set_in_array {
875 0     0 1   my ( $self, %opts ) = @_;
876              
877 0           my $to_exist = {};
878 0 0         if ( !defined( $opts{vals} ) ) {
879 0           die('Nothing specified for vars');
880             }
881             else {
882 0 0         if ( !defined $opts{vals}[0] ) {
883 0           return;
884             }
885              
886 0           my $int = 0;
887 0           while ( defined( $opts{vals}[$int] ) ) {
888 0           $to_exist->{ $opts{vals}[$int] } = 1;
889 0           $int++;
890             }
891              
892             }
893              
894 0 0         if ( !defined( $opts{dedup} ) ) {
895 0           $opts{dedup} = 1;
896             }
897              
898 0 0         if ( !defined( $opts{var} ) ) {
    0          
899 0           die('Nothing specified for vals');
900             }
901             elsif ( $opts{var} !~ /^\./ ) {
902 0           $opts{var} = '.' . $opts{var};
903             }
904              
905 0 0         if ( $opts{var} =~ /\[\]$/ ) {
906 0           $opts{var} =~ s/\[\]$//;
907             }
908              
909 0           my $string;
910 0 0         if ( !$self->is_defined( var => $opts{var} ) ) {
911 0           $string = `yq -i '$opts{var}=[]' $self->{qfile}`;
912             }
913             else {
914 0 0         if ( !$self->is_array( var => $opts{var} ) ) {
915 0           die( '"' . $opts{var} . '" is not a array or is undef' );
916             }
917              
918             }
919              
920 0           $string = `yq "$opts{var}" $self->{qfile} 2> /dev/null`;
921 0           my $yaml;
922 0 0         if ( $string =~ /\[\]/ ) {
923 0           print "blank\n";
924 0           $yaml = [];
925             }
926             else {
927 0           eval { $yaml = Load($string); };
  0            
928             }
929              
930 0           my $int = 0;
931 0           my @exiting_a;
932 0           my $existing_h = {};
933 0           while ( defined( $yaml->[$int] ) ) {
934 0 0         if ( defined( $to_exist->{ $yaml->[$int] } ) ) {
935 0           delete( $to_exist->{ $yaml->[$int] } );
936             }
937              
938 0           push( @exiting_a, $yaml->[$int] );
939              
940 0           $existing_h->{ $yaml->[$int] } = 1;
941              
942 0           $int++;
943             }
944              
945 0           my @new_array;
946 0 0         if ( $opts{dedup} ) {
947 0           push( @new_array, keys( %{$existing_h} ) );
  0            
948 0           push( @new_array, keys( %{$to_exist} ) );
  0            
949             }
950             else {
951 0           push( @new_array, @exiting_a );
952 0           push( @new_array, keys( %{$to_exist} ) );
  0            
953             }
954              
955 0           $self->set_array( var => $opts{var}, vals => \@new_array );
956             }
957              
958             =head1 AUTHOR
959              
960             Zane C. Bowers-Hadley, C<< >>
961              
962             =head1 BUGS
963              
964             Please report any bugs or feature requests to C, or through
965             the web interface at L. I will be notified, and then you'll
966             automatically be notified of progress on your bug as I make changes.
967              
968              
969              
970              
971             =head1 SUPPORT
972              
973             You can find documentation for this module with the perldoc command.
974              
975             perldoc YAML::yq::Helper
976              
977              
978             You can also look for information at:
979              
980             =over 4
981              
982             =item * RT: CPAN's request tracker (report bugs here)
983              
984             L
985              
986             =item * CPAN Ratings
987              
988             L
989              
990             =item * Search CPAN
991              
992             L
993              
994             =back
995              
996              
997             =head1 ACKNOWLEDGEMENTS
998              
999              
1000             =head1 LICENSE AND COPYRIGHT
1001              
1002             This software is Copyright (c) 2022 by Zane C. Bowers-Hadley.
1003              
1004             This is free software, licensed under:
1005              
1006             The Artistic License 2.0 (GPL Compatible)
1007              
1008              
1009             =cut
1010              
1011             1; # End of YAML::yq::Helper