File Coverage

blib/lib/Env/Assert/Functions.pm
Criterion Covered Total %
statement 88 92 95.6
branch 19 26 73.0
condition 10 15 66.6
subroutine 16 16 100.0
pod 3 3 100.0
total 136 152 89.4


line stmt bran cond sub pod time code
1             ## no critic (ControlStructures::ProhibitPostfixControls)
2             ## no critic (ControlStructures::ProhibitCascadingIfElse)
3             ## no critic (ValuesAndExpressions::ProhibitConstantPragma)
4             package Env::Assert::Functions;
5 6     6   739498 use strict;
  6         21  
  6         256  
6 6     6   37 use warnings;
  6         152  
  6         392  
7 6     6   130 use 5.010;
  6         23  
8              
9             # ABSTRACT: The functionality of Env::Assert and bin/envassert.
10              
11             our $VERSION = '0.015';
12              
13 6     6   41 use Exporter 'import';
  6         11  
  6         790  
14             our @EXPORT_OK = qw(
15             assert
16             report_errors
17             file_to_desc
18             ENV_ASSERT_MISSING_FROM_ENVIRONMENT
19             ENV_ASSERT_INVALID_CONTENT_IN_VARIABLE
20             ENV_ASSERT_MISSING_FROM_DEFINITION
21             );
22             our %EXPORT_TAGS = (
23             'all' => [
24             qw(
25             assert
26             report_errors
27             file_to_desc
28             ENV_ASSERT_MISSING_FROM_ENVIRONMENT
29             ENV_ASSERT_INVALID_CONTENT_IN_VARIABLE
30             ENV_ASSERT_MISSING_FROM_DEFINITION
31             )
32             ],
33             'constants' => [
34             qw(
35             ENV_ASSERT_MISSING_FROM_ENVIRONMENT
36             ENV_ASSERT_INVALID_CONTENT_IN_VARIABLE
37             ENV_ASSERT_MISSING_FROM_DEFINITION
38             )
39             ],
40             );
41              
42 6     6   41 use Cwd qw( abs_path );
  6         15  
  6         505  
43 6     6   2901 use English qw( -no_match_vars );
  6         12962  
  6         57  
44 6     6   2875 use File::Spec;
  6         21  
  6         209  
45 6     6   3136 use IO::File;
  6         77262  
  6         1045  
46 6     6   90 use English qw( -no_match_vars ); # Avoids regex performance penalty in perl 5.18 and earlier
  6         18  
  6         68  
47 6     6   2283 use Carp;
  6         15  
  6         572  
48              
49             use constant {
50 6         885 ENV_ASSERT_MISSING_FROM_ENVIRONMENT => 1,
51             ENV_ASSERT_INVALID_CONTENT_IN_VARIABLE => 2,
52             ENV_ASSERT_MISSING_FROM_DEFINITION => 3,
53 6     6   43 };
  6         31  
54              
55             use constant {
56 6         8887 DEFAULT_PARAMETER_BREAK_AT_FIRST_ERROR => 0,
57             INDENT => q{ },
58 6     6   43 };
  6         12  
59              
60             sub assert {
61 8     8 1 307939 my ( $env, $want, $params ) = @_;
62 8 50       31 $params = {} if !$params;
63 8 50 33     56 croak 'Invalid options. Not a hash' if ( ref $env ne 'HASH' || ref $want ne 'HASH' );
64              
65             # Set default options
66 8   100     39 $params->{'break_at_first_error'} //= DEFAULT_PARAMETER_BREAK_AT_FIRST_ERROR;
67              
68 8         13 my $success = 1;
69 8         15 my %errors;
70 8         24 my $vars = $want->{'variables'};
71 8         16 my $opts = $want->{'options'};
72 8         14 foreach my $var_name ( keys %{$vars} ) {
  8         29  
73 14         38 my $var = $vars->{$var_name};
74 14   100     48 my $required = $var->{'required'} // 1;
75 14   50     38 my $regexp = $var->{'regexp'} // q{.*};
76 14 100 66     814 if ( ( $opts->{'exact'} || $required ) && !defined $env->{$var_name} ) {
    100 66        
77 3         7 $success = 0;
78 3         15 $errors{'variables'}->{$var_name} = {
79             type => ENV_ASSERT_MISSING_FROM_ENVIRONMENT,
80             message => "Variable $var_name is missing from environment",
81             };
82 3 50       12 goto EXIT if ( $params->{'break_at_first_error'} );
83             }
84             elsif ( $env->{$var_name} !~ m/$regexp/msx ) {
85 1         4 $success = 0;
86 1         7 $errors{'variables'}->{$var_name} = {
87             type => ENV_ASSERT_INVALID_CONTENT_IN_VARIABLE,
88             message => "Variable $var_name has invalid content",
89             };
90 1 50       7 goto EXIT if ( $params->{'break_at_first_error'} );
91             }
92             }
93 8 100       29 if ( $opts->{'exact'} ) {
94 4         6 foreach my $var_name ( keys %{$env} ) {
  4         14  
95 6 100       16 if ( !exists $vars->{$var_name} ) {
96 2         4 $success = 0;
97 2         9 $errors{'variables'}->{$var_name} = {
98             type => ENV_ASSERT_MISSING_FROM_DEFINITION,
99             message => "Variable $var_name is missing from description",
100             };
101 2 50       9 goto EXIT if ( $params->{'break_at_first_error'} );
102             }
103             }
104             }
105              
106             EXIT:
107 8         50 return { success => $success, errors => \%errors, };
108             }
109              
110             sub report_errors {
111 3     3 1 292185 my ($errors) = @_;
112 3         5 my $out = q{};
113 3         16 $out .= sprintf "Environment Assert: ERRORS:\n";
114 3         3 foreach my $error_area_name ( sort keys %{$errors} ) {
  3         8  
115 2         4 $out .= sprintf "%s%s:\n", INDENT, $error_area_name;
116 2         2 foreach my $error_key ( sort keys %{ $errors->{$error_area_name} } ) {
  2         11  
117 3         7 $out .= sprintf "%s%s: %s\n", INDENT . INDENT, $error_key, $errors->{$error_area_name}->{$error_key}->{'message'};
118             }
119             }
120 3         7 return $out;
121             }
122              
123             sub file_to_desc {
124 3     3 1 19 my @rows = @_;
125 3         13 my %desc = ( 'options' => {}, 'variables' => {}, );
126 3         9 foreach (@rows) {
127              
128             # This is envassert meta command
129             ## no critic (RegularExpressions::ProhibitComplexRegexes)
130 17 50       117 if (
    100          
    100          
    50          
131             m{
132             ^ [[:space:]]{0,} [#]{2}
133             [[:space:]]{1,} envassert [[:space:]]{1,}
134             [(] opts: [[:space:]]{0,} (? .*) [)]
135             [[:space:]]{0,} $
136             }msx
137             )
138             {
139 0         0 my $opts = _interpret_opts( $LAST_PAREN_MATCH{opts} );
140 0         0 foreach ( keys %{$opts} ) {
  0         0  
141 0         0 $desc{'options'}->{$_} = $opts->{$_};
142             }
143             }
144             elsif (
145             # This is comment row
146             m{
147             ^ [[:space:]]{0,} [#]{1} .* $
148             }msx
149             )
150             {
151 5         10 1;
152             }
153             elsif (
154             # This is empty row
155             m{
156             ^ [[:space:]]{0,} $
157             }msx
158             )
159             {
160 5         10 1;
161             }
162             elsif (
163             # This is env var description
164             m{
165             ^ (? [^=]{1,}) = (? .*) $
166             }msx
167             )
168             {
169 7         86 $desc{'variables'}->{ $LAST_PAREN_MATCH{name} } = { regexp => $LAST_PAREN_MATCH{value} };
170             }
171             }
172 3         14 return \%desc;
173             }
174              
175             # Private subroutines
176              
177             sub _interpret_opts {
178 6     6   286356 my ($opts_str) = @_;
179 6         46 my @opts = split qr{
180             [[:space:]]{0,} [,] [[:space:]]{0,}
181             }msx, $opts_str;
182 6         19 my %opts;
183 6         10 foreach (@opts) {
184 11         30 my ( $key, $val ) = split qr/=/msx;
185 11         22 $opts{$key} = $val;
186             }
187 6         19 return \%opts;
188             }
189              
190             1;
191              
192             __END__