File Coverage

blib/lib/String/Interpolate/RE.pm
Criterion Covered Total %
statement 48 48 100.0
branch 32 32 100.0
condition 17 18 94.4
subroutine 8 8 100.0
pod n/a
total 105 106 99.0


line stmt bran cond sub pod time code
1             package String::Interpolate::RE;
2              
3             # ABSTRACT: interpolate variables into strings using regular expressions
4              
5 6     6   1377580 use strict;
  6         43  
  6         179  
6 6     6   32 use warnings;
  6         12  
  6         181  
7              
8 6     6   2805 use Exporter::Shiny qw[ strinterp ];
  6         25742  
  6         40  
9              
10             our $VERSION = '0.11';
11              
12             ## no critic (ProhibitAccessOfPrivateData)
13              
14             my %Opt = (
15             variable_re => qr/\w+/,
16             raiseundef => 0,
17             emptyundef => 0,
18             useenv => 1,
19             format => 0,
20             recurse => 0,
21             recurse_limit => 0,
22             recurse_fail_limit => 100,
23             );
24              
25             *strinterp = _mk_strinterp( \%Opt );
26              
27             sub _croak {
28 10     10   71 require Carp;
29 10         1758 goto &Carp::croak;
30             }
31              
32             sub _generate_strinterp {
33              
34 6     6   713 my ( $me, $name, $args ) = @_;
35              
36             return \&strinterp
37 6 100 100     61 if ! defined $args || ! defined $args->{opts};
38              
39 1         8 my %opt = %Opt;
40 1         3 $opt{lc $_} = $args->{opts}{$_} foreach keys %{$args->{opts}};
  1         7  
41 1         4 return _mk_strinterp( \%opt );
42             }
43              
44             sub _mk_strinterp {
45              
46 7     7   18 my $default_opt = shift;
47              
48             return sub {
49              
50 50     50   29754 my ( $text, $var, $opts ) = @_;
51              
52 50 100       158 $var = {} unless defined $var;
53              
54 50         269 my %opt = %$default_opt;
55              
56 50 100       144 if ( defined $opts ) {
57 27         134 $opt{ lc $_ } = $opts->{$_} foreach keys %$opts;
58             }
59             ## use critic
60              
61 50 100       151 my $fmt = $opt{format} ? ':([^}]+)' : '()';
62              
63 50         102 $opt{track} = {};
64 50         89 $opt{loop} = 0;
65 50         89 $opt{fmt} = $fmt;
66              
67 50         157 _strinterp( $text, $var, \%opt );
68              
69 40         284 return $text;
70             }
71 7         45 }
72              
73             sub _strinterp {
74              
75 95     95   138 my $var = $_[1];
76 95         130 my $opt = $_[2];
77 95         140 my $fmt = $opt->{fmt};
78 95         151 my $re = $opt->{variable_re};
79              
80 95         1071 $_[0] =~ s{
81             \$ # find a literal dollar sign
82             ( # followed by either
83             \{ ($re)(?:$fmt)? \} # a variable name in curly brackets ($2)
84             # and an optional sprintf format
85             | # or
86             (\w+) # a bareword ($3)
87             )
88             }{
89 103 100       393 my $t = defined $4 ? $4 : $2;
90              
91 103 100       287 my $user_value = 'CODE' eq ref $var ? $var->($t) : $var->{$t};
92              
93             my $v =
94             # user provided?
95             defined $user_value ? $user_value
96              
97             # maybe in the environment
98             : $opt->{useenv} && exists $ENV{$t} ? $ENV{$t}
99              
100             # undefined: throw an error?
101 103 100 100     302 : $opt->{raiseundef} ? _croak( "undefined variable: $t\n" )
    100          
    100          
102              
103             # undefined
104             : undef
105              
106             ;
107              
108 100 100 66     319 if ( $opt->{recurse} && defined $v ) {
109              
110              
111             RECURSE:
112             {
113              
114 55         81 _croak(
115             "circular interpolation loop detected with repeated interpolation of <\$$t>\n"
116 55 100       148 ) if $opt->{track}{$t}++;
117              
118 51         69 ++$opt->{loop};
119              
120 51 100 100     114 last RECURSE if $opt->{recurse_limit} && $opt->{loop} > $opt->{recurse_limit};
121              
122             _croak(
123             "recursion fail-safe limit ($opt->{recurse_fail_limit}) reached at interpolation of <\$$t>\n"
124 48 100 100     155 ) if $opt->{recurse_fail_limit} && $opt->{loop} > $opt->{recurse_fail_limit};
125              
126 45         90 _strinterp( $v, $_[1], $_[2] );
127              
128             }
129              
130 32         67 delete $opt->{track}{$t};
131 32         44 --$opt->{loop};
132             }
133              
134             # if not defined:
135             # if emptyundef, replace with an empty string
136             # otherwise, just put it back into the string
137 77 100 100     451 ! defined $v ? ( $opt->{emptyundef} ? '' : '$' . $1 )
    100          
    100          
138              
139             # no format? return as is
140             : ! defined $3 || $3 eq '' ? $v
141              
142             # format it
143             : sprintf( $3, $v)
144              
145             ;
146              
147             }egx;
148             }
149              
150             1;
151              
152             #
153             # This file is part of String-Interpolate-RE
154             #
155             # This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
156             #
157             # This is free software, licensed under:
158             #
159             # The GNU General Public License, Version 3, June 2007
160             #
161              
162             __END__