File Coverage

blib/lib/Config/AWS.pm
Criterion Covered Total %
statement 34 34 100.0
branch 12 12 100.0
condition 10 13 76.9
subroutine 15 15 100.0
pod 6 9 66.6
total 77 83 92.7


line stmt bran cond sub pod time code
1             package Config::AWS;
2             # ABSTRACT: Parse AWS config files
3              
4 6     6   1292449 use strict;
  6         56  
  6         175  
5 6     6   43 use warnings;
  6         12  
  6         140  
6              
7 6     6   28 use Carp ();
  6         17  
  6         87  
8 6     6   2923 use Ref::Util;
  6         10123  
  6         307  
9 6     6   48 use Scalar::Util;
  6         14  
  6         299  
10 6         40 use Exporter::Shiny qw(
11             read
12             read_all
13             list_profiles
14             read_file
15             read_string
16             read_handle
17             config_file
18             credentials_file
19             default_profile
20 6     6   2690 );
  6         25271  
21              
22             our $VERSION = '0.12';
23             our %EXPORT_TAGS = (
24             ini => [qw( read_file read_string read_handle )],
25             aws => [qw( config_file default_profile credentials_file )],
26             read => [qw( read read_all list_profiles )],
27             all => [qw( :ini :aws :read )],
28             );
29              
30             # Internal methods for parsing and validation
31              
32             my $prepare = sub {
33             # Input is given argument or credentials file (if exists) or config file.
34             my $input = shift // do {
35             my $cred_file = credentials_file();
36             -r $cred_file ? $cred_file : config_file();
37             };
38              
39             unless ( Ref::Util::is_ref $input ) {
40             require Path::Tiny;
41             my @lines = eval { Path::Tiny::path( $input )->lines };
42             if ($@) {
43             Carp::croak "Cannot read from $input: $@->{err}"
44             if ref $@ && $@->isa('Path::Tiny::Error');
45             Carp::croak $@;
46             }
47             return \@lines;
48             }
49              
50             return [ $input->getlines ] if Scalar::Util::openhandle $input;
51              
52             if ( Ref::Util::is_blessed_ref $input ) {
53             return [ $input->slurp ] if $input->isa('Path::Class::File');
54             return [ $input->lines ] if $input->isa('Path::Tiny');
55              
56             Carp::croak 'Cannot read from objects of type ', ref $input;
57             }
58              
59             return [ split /\R/, $$input ] if Ref::Util::is_scalarref $input;
60             return $input if Ref::Util::is_arrayref $input;
61              
62             Carp::croak "Could not use $input as source for ", (caller 1)[3];
63             };
64              
65             my $read = sub {
66             my ($lines, $target_profile) = @_;
67              
68             Carp::carp 'Reading config with only one line or less. Faulty input?'
69             if @$lines <= 1;
70              
71             my $hash = {};
72             my $nested = {};
73             my $profile = '';
74              
75             for my $i (0 .. $#$lines) {
76             my $line = $lines->[$i];
77             $line =~ s/\R$//;
78              
79             if ($line =~ /^\[(?:profile )?([\w\/.@%:_-]+)\]/) {
80             $profile = $1;
81             next;
82             }
83              
84             next if $target_profile && $profile ne $target_profile;
85              
86             next unless my ($indent, $key, $value) = $line =~ /^(\s*)(\w+)\s*=\s*(.*)/;
87              
88             if (length $indent) {
89             $nested->{$key} = $value;
90             }
91             else {
92             # Add nested hash if the value is empty and next line is indented.
93             $hash->{$profile}{$key}
94             = !length $value && $i < $#$lines && $lines->[$i + 1] =~ /^\s+/
95             ? $nested : $value;
96              
97             $nested = {} if keys %$nested;
98             }
99             }
100              
101             return $target_profile ? ( $hash->{$target_profile} // {} ) : $hash;
102             };
103              
104             # Config parsing interface
105              
106             sub read {
107 37   66 37 1 226628 $read->( $prepare->(shift), shift // default_profile() );
108             }
109              
110             sub read_all {
111 4     4 1 14529 $read->( &$prepare );
112             }
113              
114             sub list_profiles {
115 9     9 1 102801 map /^\[(?:profile )?([\w\/.@%:_-]+)\]/, @{ &$prepare };
  9         27  
116             }
117              
118             # AWS information methods
119              
120             sub default_profile {
121 20   100 20 1 52984 $ENV{AWS_DEFAULT_PROFILE} // 'default';
122             }
123              
124             sub credentials_file {
125 13   66 13 1 4113 $ENV{AWS_SHARED_CREDENTIALS_FILE} // ( glob '~/.aws/credentials' )[0];
126             }
127              
128             sub config_file {
129 11   66 11 1 4351 $ENV{AWS_CONFIG_FILE} // ( glob '~/.aws/config' )[0];
130             }
131              
132             # Methods for compatibility with Config::INI interface
133              
134             sub read_file {
135 6 100   6 0 20253 Carp::croak 'Filename is missing' unless @_;
136 5 100       199 Carp::croak 'Argument was not a string' if Ref::Util::is_ref $_[0];
137 3         11 $read->( $prepare->(shift), @_ );
138             }
139              
140             sub read_string {
141 6 100   6 0 109399 Carp::croak 'String is missing' unless @_;
142 5 100       199 Carp::croak 'Argument was not a string' if Ref::Util::is_ref $_[0];
143 3   100     49 $read->( [ split /\R/, shift // '' ], @_ );
144             }
145              
146             sub read_handle {
147 5 100   5 0 17977 Carp::croak 'Handle is missing' unless @_;
148 4 100       223 Carp::croak 'Argument was not a handle' unless Scalar::Util::openhandle $_[0];
149 2         66 $read->( [ shift->getlines ], @_ );
150             }
151              
152             1;
153              
154             __END__