File Coverage

blib/lib/String/Interpolate/Shell.pm
Criterion Covered Total %
statement 58 64 90.6
branch 34 46 73.9
condition 5 8 62.5
subroutine 11 11 100.0
pod 1 1 100.0
total 109 130 83.8


, or C,
line stmt bran cond sub pod time code
1             package String::Interpolate::Shell;
2              
3             # ABSTRACT: Variable interpolation, shell style
4              
5 3     3   50766 use strict;
  3         12  
  3         96  
6 3     3   18 use warnings;
  3         6  
  3         96  
7              
8 3     3   1890 use Text::Balanced qw[ extract_bracketed extract_multiple extract_quotelike];
  3         64272  
  3         439  
9 3     3   2059 use Params::Check qw[ check ];
  3         17513  
  3         263  
10 3     3   116 use Carp;
  3         10  
  3         196  
11              
12 3     3   24 use Exporter 'import';
  3         7  
  3         637  
13              
14             our @EXPORT_OK = qw[ strinterp ];
15              
16             our $VERSION = '0.02';
17              
18             sub _extract {
19              
20             extract_multiple( $_[0],
21             [
22             qr/\s+/,
23             qr/\\(\\)/,
24             qr/\\(\$)/,
25             { V => qr/\$(\w+)/ },
26 30     30   4653 { B => sub { (extract_bracketed( $_[0], '{}', qr/\$/ ))[0] } },
27 21     21   247 ] );
28             }
29              
30             sub _handle_undef {
31              
32 13     13   48 my ( $var, $q, $attr, $rep ) = @_;
33              
34             ## no critic(ProhibitAccessOfPrivateData)
35              
36 13 100       79 return $var->{$q} if defined $var->{$q};
37              
38 3     3   2296 no if $] >= 5.022, warnings => 'redundant';
  3         49  
  3         23  
39              
40             carp( sprintf( $attr->{undef_message}, $rep) )
41 5 100       145 if $attr->{undef_verbosity} eq 'warn';
42              
43             croak( sprintf( $attr->{undef_message}, $rep) )
44 5 100       493 if $attr->{undef_verbosity} eq 'fatal';
45              
46 3 100       22 return $rep if $attr->{undef_value} eq 'ignore';
47             }
48              
49             sub strinterp{
50              
51 21     21 1 12710 my ( $text, $var, $attr ) = @_;
52              
53             ## no critic(ProhibitAccessOfPrivateData)
54 21 50 100     283 $attr = check( {
55             undef_value => { allow => [ qw[ ignore remove ] ],
56             default => 'ignore' },
57             undef_verbosity => { allow => [ qw[ silent warn fatal ] ],
58             default => 'silent' },
59             undef_message => { default => "undefined variable: %s\n" },
60             }, $attr || {} )
61             or croak( "error parsing arguments: ", Params::Check::last_error() );
62              
63 21         2251 my @matches;
64              
65 21         66 for my $matchstr ( _extract($text ) ) {
66              
67 25         3835 my $ref = ref $matchstr;
68              
69 25 100       90 if ( 'B' eq $ref ) {
    100          
    50          
70              
71             # remove enclosing brackets
72 13         51 my $match = substr( $$matchstr, 1,-1 );
73              
74             # see if there's a 'shell' modifier expression
75 13         109 my ( $ind, $q, $modf, $rest ) = $match =~ /^(!)?(\w+)(:[-?=+~:])?(.*)/;
76              
77             # if there's no modifier but there is trailing cruft, it's an error
78 13 50 66     84 die( "unrecognizeable variable name: \${$match}\n")
79             if ! defined $modf && $rest ne '';
80              
81             # if indirect flag is set, expand variable
82 13 100       43 if ( defined $ind ) {
83              
84 1 50       4 if ( defined $var->{$q} ) {
85 1         4 $q = $var->{$q};
86             }
87             else
88             {
89 0         0 push @matches, _handle_undef( $attr, '$' . $$matchstr );
90 0         0 next;
91             }
92             }
93              
94              
95 13 100       70 if ( ! defined $modf ) {
    100          
    100          
    50          
    100          
    100          
    50          
96              
97 4         24 push @matches, _handle_undef( $var, $q, $attr, '$' . $$matchstr );
98             }
99              
100             elsif ( ':?' eq $modf ) {
101              
102 2         11 local $attr->{undef_verbosity} = 'fatal';
103 2         7 local $attr->{undef_message} = $rest;
104              
105 2         17 push @matches, _handle_undef( $var, $q, $attr, '$' . $$matchstr );
106              
107             }
108             elsif ( ':-' eq $modf ) {
109              
110 1 50       15 push @matches, defined $var->{$q} ? $var->{$q} : strinterp( $rest, $var, $attr );
111              
112             }
113              
114             elsif ( ':=' eq $modf ) {
115              
116              
117             $var->{$q} = strinterp( $rest, $var, $attr )
118 0 0       0 unless defined $var->{$q};
119 0         0 push @matches, $var->{$q};
120              
121             }
122              
123             elsif ( ':+' eq $modf ) {
124              
125             push @matches, strinterp( $rest, $var, $attr )
126 2 50       12 if defined $var->{$q};
127              
128             }
129              
130             elsif ( '::' eq $modf ) {
131              
132 1         6 push @matches, sprintf( $rest, _handle_undef( $var, $q, $attr, '$' . $$matchstr ) );
133              
134             }
135              
136             elsif ( ':~' eq $modf ) {
137              
138 3         13 my ( $expr, $xtra, $op ) = (extract_quotelike( $rest ))[0,1,3];
139 3 50 33     482 die( "unable to parse variable substitution command: $rest\n" )
140             if $xtra !~ /^\s*$/ or $op !~ /^(s|tr|y)$/;
141              
142 3         8 my $t = $var->{$q};
143             ## no critic(ProhibitStringyEval)
144 3         179 eval "\$t =~ $expr";
145 3 50       13 die $@ if $@;
146              
147 3         11 push @matches, $t;
148              
149             }
150              
151 0         0 else { die( "internal error" ) }
152              
153             }
154             elsif ( 'V' eq $ref ) {
155              
156 6         17 my $q = $$matchstr;
157              
158 6         28 push @matches, _handle_undef( $var, $q, $attr, "\$$q" );
159              
160             }
161              
162             elsif ( $ref ) {
163              
164 0         0 push @matches, $$matchstr;
165              
166             }
167             else {
168              
169 6         16 push @matches, $matchstr;
170              
171             }
172              
173              
174             }
175              
176 19         210 return join('', @matches);
177              
178             }
179              
180             1;
181             #
182             # This file is part of String-Interpolate-Shell
183             #
184             # This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
185             #
186             # This is free software, licensed under:
187             #
188             # The GNU General Public License, Version 3, June 2007
189             #
190              
191             =pod
192              
193             =head1 NAME
194              
195             String::Interpolate::Shell - Variable interpolation, shell style
196              
197             =head1 VERSION
198              
199             version 0.02
200              
201             =head1 SYNOPSIS
202              
203             use String::Interpolate::Shell qw[ strinterp ];
204              
205             $interpolated_text = strinterp( $text, \%var, \%attr );
206              
207             =head1 DESCRIPTION
208              
209             B interpolates variables into strings.
210             Variables are specified using a syntax similar to that use by B.
211             Undefined variables can be silently ignored, removed from the string,
212             can cause warnings to be issued or errors to be thrown.
213              
214             =over
215              
216             =item $I
217              
218             Insert the value of the variable.
219              
220             =item ${I}
221              
222             Insert the value of the variable.
223              
224             =item ${I:?error message}
225              
226             Insert the value of the variable. If it is not defined,
227             the routine croaks with the specified message.
228              
229             =item ${I:-I}
230              
231             Insert the value of the variable. If it is not defined,
232             process the specified default text for any variable interpolations and
233             insert the result.
234              
235             =item ${I:+I}
236              
237             If the variable is defined, insert the result of interpolating
238             any variables into the default text.
239              
240             =item ${I:=I}
241              
242             Insert the value of the variable. If it is not defined,
243             insert the result of interpolating any variables into the default text
244             and set the variable to the same value.
245              
246             =item ${I::I}
247              
248             Insert the value of the variable as formatted according to the
249             specified B compatible format.
250              
251             =item ${I:~I/I/I/msixpogce}
252              
253             Insert the modified value of the variable. The modification is
254             specified by I, which may be any of C, C
255             corresponding to the Perl operators of the same name. Delimiters for
256             the modification may be any of those recognized by Perl. The
257             modification is performed using a Perl string B.
258              
259             =back
260              
261             In any of the bracketed forms, if the variable name is preceded with an exclamation mark (C)
262             the name of the variable to be interpreted is taken from the value of the specified variable.
263              
264             =head1 FUNCTIONS
265              
266             =over
267              
268             =item strinterp
269              
270             $interpolated_text = strinterp( $template, \%var, \%attr );
271              
272             Return a string containing a copy of C<$template> with variables interpolated.
273              
274             C<%var> contains the variable names and values.
275              
276             C<%attr> may contain the following entries:
277              
278             =over
279              
280             =item undef_value
281              
282             This indicates how undefined variables should be interpolated
283              
284             =over
285              
286             =item C
287              
288             Ignore them. The token in C<$text> is left as is.
289              
290             =item C
291              
292             Remove the token from C<$text>.
293              
294             =back
295              
296             =item undef_verbosity
297              
298             This indicates how undefined variables should be reported.
299              
300             =over
301              
302             =item C
303              
304             No message is returned.
305              
306             =item C
307              
308             A message is output via C.
309              
310             =item C
311              
312             A message is output via C.
313              
314             =back
315              
316             =back
317              
318             =back
319              
320             =head1 BUGS AND LIMITATIONS
321              
322             You can make new bug reports, and view existing ones, through the
323             web interface at L.
324              
325             =head1 AUTHOR
326              
327             Diab Jerius
328              
329             =head1 COPYRIGHT AND LICENSE
330              
331             This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
332              
333             This is free software, licensed under:
334              
335             The GNU General Public License, Version 3, June 2007
336              
337             =cut
338              
339             __END__