File Coverage

blib/lib/Log/Report/Extract/Template.pm
Criterion Covered Total %
statement 76 82 92.6
branch 18 30 60.0
condition 4 11 36.3
subroutine 11 11 100.0
pod 0 5 0.0
total 109 139 78.4


line stmt bran cond sub pod time code
1             # Copyrights 2007-2025 by [Mark Overmeer ].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.03.
5             # This code is part of distribution Log-Report-Lexicon. Meta-POD processed
6             # with OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package Log::Report::Extract::Template;{
10             our $VERSION = '1.12';
11             }
12              
13 1     1   761 use base 'Log::Report::Extract';
  1         3  
  1         839  
14              
15 1     1   8 use warnings;
  1         2  
  1         53  
16 1     1   7 use strict;
  1         2  
  1         31  
17              
18 1     1   7 use Log::Report 'log-report-lexicon';
  1         2  
  1         5  
19              
20              
21             sub init($)
22 1     1 0 4 { my ($self, $args) = @_;
23 1         9 $self->SUPER::init($args);
24             $self->{LRET_domain} = $args->{domain}
25 1 50       6 or error "template extract requires explicit domain";
26              
27 1         3 $self->{LRET_pattern} = $args->{pattern};
28 1         3 $self;
29             }
30              
31             #----------
32              
33 2     2 0 19 sub domain() {shift->{LRET_domain}}
34 1     1 0 7 sub pattern() {shift->{LRET_pattern}}
35              
36             #----------
37              
38             sub process($@)
39 1     1 0 893 { my ($self, $fn, %opts) = @_;
40              
41 1   50     9 my $charset = $opts{charset} || 'utf-8';
42 1         7 info __x"processing file {fn} in {charset}", fn=> $fn, charset => $charset;
43              
44 1 50 33     137 my $pattern = $opts{pattern} || $self->pattern
45             or error __"need pattern to scan for, either via new() or process()";
46              
47             # Slurp the whole file
48 1         4 local *IN;
49 1 50   1   961 open IN, "<:encoding($charset)", $fn
  1         22  
  1         6  
  1         75  
50             or fault __x"cannot read template from {fn}", fn => $fn;
51              
52 1         1600 undef $/;
53 1         78 my $text = ;
54 1         51 close IN;
55              
56 1         8 my $domain = $self->domain;
57 1         11 $self->_reset($domain, $fn);
58              
59 1 50       9 if(ref $pattern eq 'CODE')
    50          
60 0         0 { return $pattern->($fn, \$text);
61             }
62             elsif($pattern =~ m/^TT([12])-(\w+)$/)
63 1         5 { return $self->scanTemplateToolkit($1, $2, $fn, \$text);
64             }
65             else
66 0         0 { error __x"unknown pattern {pattern}", pattern => $pattern;
67             }
68 0         0 ();
69             }
70              
71             sub _no_escapes_in($$$$)
72 12     12   34 { my ($msgid, $plural, $fn, $linenr) = @_;
73 12 100 33     78 return if $msgid !~ /\&\w+\;/
    50          
74             && (defined $plural ? $plural !~ /\&\w+\;/ : 1);
75 0 0       0 $msgid .= "|$plural" if defined $plural;
76              
77 0         0 warning __x"msgid '{msgid}' contains html escapes, don't do that. File {fn} line {linenr}"
78             , msgid => $msgid, fn => $fn, linenr => $linenr;
79             }
80              
81             sub scanTemplateToolkit($$$$)
82 1     1 0 7 { my ($self, $version, $function, $fn, $textref) = @_;
83              
84             # Split the whole file on the pattern in four fragments per match:
85             # (text, leading, needed trailing, text, leading, ...)
86             # f.i. ('', '[% loc("', 'some-msgid', '", params) %]', ' more text')
87 1 50       79 my @frags = $version==1
88             ? split(/[\[%]%(.*?)%[%\]]/s, $$textref)
89             : split(/\[%(.*?)%\]/s, $$textref);
90              
91 1         7 my $domain = $self->domain;
92 1         19 my $linenr = 1;
93 1         2 my $msgs_found = 0;
94              
95             # pre-compile the regexes, for performance
96 1         124 my $pipe_func_block = qr/^\s*(?:\|\s*|FILTER\s+)$function\b/;
97 1         134 my $msgid_pipe_func = qr/^\s*(["'])([^\r\n]+?)\1\s*\|\s*$function\b/;
98 1         62 my $func_msgid_multi = qr/(\b$function\s*\(\s*)(["'])([^\r\n]+?)\2/s;
99              
100 1         27 while(@frags > 2)
101 12         36 { my ($skip_text, $take) = (shift @frags, shift @frags);
102 12         30 $linenr += $skip_text =~ tr/\n//;
103 12 100       130 if($take =~ $pipe_func_block)
104             { # [% | loc(...) %] $msgid [%END%] or [% FILTER ... %]...[% END %]
105 1 50 33     12 if(@frags < 2 || $frags[1] !~ /^\s*END\s*$/)
106 0         0 { error __x"template syntax error, no END in {fn} line {line}"
107             , fn => $fn, line => $linenr;
108             }
109 1         3 my $msgid = $frags[0]; # next content
110 1 50       6 my $plural = $msgid =~ s/\|(.*)// ? $1 : undef;
111 1         20 _no_escapes_in $msgid, $plural, $fn, $linenr;
112              
113 1         5 $self->store($domain, $fn, $linenr, $msgid, $plural);
114 1         2 $msgs_found++;
115              
116 1         3 $linenr += $take =~ tr/\n//;
117 1         4 next;
118             }
119              
120 11 100       63 if($take =~ $msgid_pipe_func)
121             { # [% $msgid | loc(...) %]
122 1         4 my $msgid = $2;
123 1 50       6 my $plural = $msgid =~ s/\|(.*)// ? $1 : undef;
124 1         5 _no_escapes_in $msgid, $plural, $fn, $linenr;
125              
126 1         5 $self->store($domain, $fn, $linenr, $msgid, $plural);
127 1         2 $msgs_found++;
128              
129 1         3 $linenr += $take =~ tr/\n//;
130 1         5 next;
131             }
132              
133             # loc($msgid, ...) form, can appear more than once
134 10         135 my @markup = split $func_msgid_multi, $take;
135 10         27 while(@markup > 4)
136             { # quads with text, call, quote, msgid
137 10         27 $linenr += ($markup[0] =~ tr/\n//)
138             + ($markup[1] =~ tr/\n//);
139 10         19 my $msgid = $markup[3];
140 10 100       37 my $plural = $msgid =~ s/\|(.*)// ? $1 : undef;
141 10         54 _no_escapes_in $msgid, $plural, $fn, $linenr;
142              
143 10         55 $self->store($domain, $fn, $linenr, $msgid, $plural);
144 10         20 $msgs_found++;
145 10         41 splice @markup, 0, 4;
146             }
147 10         39 $linenr += $markup[-1] =~ tr/\n//; # rest of container
148             }
149             # $linenr += $frags[-1] =~ tr/\n//; # final page fragment not needed
150              
151 1         11 $msgs_found;
152             }
153              
154             #----------------------------------------------------
155              
156             1;