File Coverage

lib/Log/Report/Template/Extract.pm
Criterion Covered Total %
statement 79 85 92.9
branch 18 30 60.0
condition 4 11 36.3
subroutine 13 13 100.0
pod 3 5 60.0
total 117 144 81.2


line stmt bran cond sub pod time code
1             # This code is part of Perl distribution Log-Report-Template version 1.04.
2             # The POD got stripped from this file by OODoc version 3.05.
3             # For contributors see file ChangeLog.
4              
5             # This software is copyright (c) 2017-2025 by Mark Overmeer.
6              
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
10              
11             #oodist: *** DO NOT USE THIS VERSION FOR PRODUCTION ***
12             #oodist: This file contains OODoc-style documentation which will get stripped
13             #oodist: during its release in the distribution. You can use this file for
14             #oodist: testing, however the code of this development version may be broken!
15              
16             package Log::Report::Template::Extract;{
17             our $VERSION = '1.04';
18             }
19              
20 3     3   203375 use base 'Log::Report::Extract';
  3         7  
  3         2032  
21              
22 3     3   52483 use warnings;
  3         6  
  3         291  
23 3     3   17 use strict;
  3         6  
  3         77  
24              
25 3     3   14 use Log::Report 'log-report-template';
  3         5  
  3         21  
26              
27 3     3   1367 use Log::Report::Template::Textdomain ();
  3         7  
  3         4258  
28 8     8   27 sub _normalized_ws($) { Log::Report::Template::Textdomain::_normalized_ws($_[0]) }
29              
30             #--------------------
31              
32             sub init($)
33 3     3 0 181561 { my ($self, $args) = @_;
34 3         20 $self->SUPER::init($args);
35             $self->{LRTE_domain} = $args->{domain}
36 3 50       175 or error __"template extract requires explicit domain";
37              
38 3         15 $self->{LRTE_pattern} = $args->{pattern};
39 3         18 $self;
40             }
41              
42             #--------------------
43              
44 6     6 1 41 sub domain() { $_[0]->{LRTE_domain} }
45 3     3 1 18 sub pattern() { $_[0]->{LRTE_pattern} }
46              
47             #--------------------
48              
49             sub process($@)
50 3     3 1 578 { my ($self, $fn, %opts) = @_;
51              
52 3   50     18 my $charset = $opts{charset} || 'utf-8';
53 3         17 info __x"processing file {file} in {charset}", file => $fn, charset => $charset;
54              
55 3 50 33     342 my $pattern = $opts{pattern} || $self->pattern
56             or error __"need pattern to scan for, either via new() or process()";
57              
58             # Slurp the whole file
59 3 50   2   139 open my $in, "<:encoding($charset)", $fn
  2         1596  
  2         52  
  2         12  
60             or fault __x"cannot read template from {file}", file => $fn;
61              
62 3         2604 undef $/;
63 3         146 my $text = $in->getline;
64 3         15493 $in->close;
65              
66 3         121 my $domain = $self->domain;
67 3         23 $self->_reset($domain, $fn);
68              
69 3 50       6787 if(ref $pattern eq 'CODE')
    50          
70 0         0 { return $pattern->($fn, \$text);
71             }
72             elsif($pattern =~ m/^TT([12])-(\w+)$/)
73 3         17 { return $self->scanTemplateToolkit($1, $2, $fn, \$text);
74             }
75             else
76 0         0 { error __x"unknown pattern {pattern}", pattern => $pattern;
77             }
78              
79 0         0 ();
80             }
81              
82             sub _no_escapes_in($$$$)
83 18     18   32 { my ($msgid, $plural, $fn, $linenr) = @_;
84 18 100 33     100 return if $msgid !~ /\&\w+\;/ && (defined $plural ? $plural !~ /\&\w+\;/ : 1);
    50          
85 0 0       0 $msgid .= "|$plural" if defined $plural;
86              
87 0         0 warning __x"msgid '{msgid}' contains html escapes, don't do that. File {file} line {linenr}", msgid => $msgid, file => $fn, linenr => $linenr;
88             }
89              
90              
91             sub scanTemplateToolkit($$$$)
92 3     3 0 20 { my ($self, $version, $function, $fn, $textref) = @_;
93              
94             # Split the whole file on the pattern in four fragments per match:
95             # (text, leading, needed trailing, text, leading, ...)
96             # f.i. ('', '[% loc("', 'some-msgid', '", params) %]', ' more text')
97 3 50       111 my @frags = $version==1
98             ? split(/[\[%]%(.*?)%[%\]]/s, $$textref)
99             : split(/\[%(.*?)%\]/s, $$textref);
100              
101 3         12 my $domain = $self->domain;
102 3         6 my $linenr = 1;
103 3         6 my $msgs_found = 0;
104              
105             # pre-compile the regexes, for performance
106 3         303 my $pipe_func_block = qr/^\s*(?:\|\s*|FILTER\s+)$function\b/;
107 3         265 my $msgid_pipe_func = qr/^\s*(["'])([^\r\n]+?)\1\s*\|\s*$function\b/;
108 3         206 my $func_msgid_multi = qr/(\b$function\s*\(\s*)(["'])([^\r\n]+?)\2/s;
109              
110 3         20 while(@frags > 2)
111 34         77 { my ($skip_text, $take) = (shift @frags, shift @frags);
112 34         58 $linenr += $skip_text =~ tr/\n//;
113 34 100       199 if($take =~ $pipe_func_block)
114             { # [% | loc(...) %] $msgid [%END%] or [% FILTER ... %]...[% END %]
115 4 50 33     40 if(@frags < 2 || $frags[1] !~ /^\s*END\s*$/)
116 0         0 { error __x"template syntax error, no END in {fn} line {line}", fn => $fn, line => $linenr;
117             }
118 4         10 my $msgid = $frags[0]; # next content
119 4 50       14 my $plural = $msgid =~ s/\|(.*)// ? $1 : undef;
120 4         14 _no_escapes_in $msgid, $plural, $fn, $linenr;
121              
122 4         13 $self->store($domain, $fn, $linenr, _normalized_ws($msgid), _normalized_ws($plural));
123 4         310 $msgs_found++;
124              
125 4         9 $linenr += $take =~ tr/\n//;
126 4         14 next;
127             }
128              
129 30 100       124 if($take =~ $msgid_pipe_func)
130             { # [% $msgid | loc(...) %]
131 1         2 my $msgid = $2;
132 1 50       9 my $plural = $msgid =~ s/\|(.*)// ? $1 : undef;
133 1         4 _no_escapes_in $msgid, $plural, $fn, $linenr;
134              
135 1         3 $self->store($domain, $fn, $linenr, $msgid, $plural);
136 1         89 $msgs_found++;
137              
138 1         2 $linenr += $take =~ tr/\n//;
139 1         3 next;
140             }
141              
142             # loc($msgid, ...) form, can appear more than once
143 29         201 my @markup = split $func_msgid_multi, $take;
144 29         87 while(@markup > 4)
145             { # quads with text, call, quote, msgid
146 13         27 $linenr += ($markup[0] =~ tr/\n//) + ($markup[1] =~ tr/\n//);
147 13         18 my $msgid = $markup[3];
148 13 100       38 my $plural = $msgid =~ s/\|(.*)// ? $1 : undef;
149 13         34 _no_escapes_in $msgid, $plural, $fn, $linenr;
150              
151 13         55 $self->store($domain, $fn, $linenr, $msgid, $plural);
152 13         1183 $msgs_found++;
153 13         40 splice @markup, 0, 4;
154             }
155 29         101 $linenr += $markup[-1] =~ tr/\n//; # rest of container
156             }
157             # $linenr += $frags[-1] =~ tr/\n//; # final page fragment not needed
158              
159 3         31 $msgs_found;
160             }
161              
162             #--------------------
163              
164             1;