File Coverage

blib/lib/App/optex/mask.pm
Criterion Covered Total %
statement 59 62 95.1
branch 9 18 50.0
condition 2 6 33.3
subroutine 13 13 100.0
pod 1 6 16.6
total 84 105 80.0


line stmt bran cond sub pod time code
1             package App::optex::mask;
2              
3 10     10   300221 use 5.024;
  10         43  
4 10     10   61 use warnings;
  10         20  
  10         1601  
5              
6             our $VERSION = "0.01";
7              
8             =encoding utf-8
9              
10             =head1 NAME
11              
12             App::optex::mask - optex data masking module
13              
14             =head1 VERSION
15              
16             Version 0.01
17              
18             =head1 SYNOPSIS
19              
20             optex -Mmask patterns -- command
21              
22             =head1 DESCRIPTION
23              
24             App::optex::mask is an B module for masking data given as
25             standard input to a command to be executed. It transforms strings
26             matching a specified pattern according to a set of rules before giving
27             them as input to a command, and restores the resulting content to the
28             original string.
29              
30             Multiple conversion rules can be specified, but currently only C
31             is supported. This is for B translation interface, and
32             converts a string to an XML tag such as C<< >>.
33              
34             The following example translates an English sentence into French.
35              
36             $ echo All men are created equal | deepl text --to FR "$(cat)"
37             Tous les hommes sont créés égaux
38              
39             If you want to leave part of a sentence untranslated, specify a
40             pattern that matches the string.
41              
42             $ echo All men are created equal | \
43             optex -Mmask::set=debug men -- sh -c 'deepl text --to FR "$(cat)"'
44             [1] All men are created equal
45             [2] All are created equal
46             [3] Tous les sont créés égaux
47             [4] Tous les men sont créés égaux
48             Tous les men sont créés égaux
49              
50             =head1 PARAMETERS
51              
52             Parameters are given as options for C function at module startup.
53              
54             For example, to enable the debugging option, specify the following. If
55             no value is specified, it defaults to 1 and can be omitted.
56              
57             optex -Mmask::set(debug=1)
58             optex -Mmask::set(debug)
59              
60             This could be written as follows. This is somewhat easier to type
61             from the shell, since it does not use parentheses.
62              
63             optex -Mmask::set=debug=1
64             optex -Mmask::set=debug
65              
66             =over 7
67              
68             =item B
69              
70             =item B
71              
72             Enable encoding and decoding. You can check how it is encoded by
73             disabling the C option.
74              
75             =item B
76              
77             The default is C, which is the only supported at this time.
78              
79             =item B
80              
81             Specifies the initial value of the number used as id in xml tag.
82             Default is 1.
83              
84             =item B
85              
86             Enable debugging.
87              
88             =back
89              
90             =head1 INSTALL
91              
92             =head2 CPANM
93              
94             cpanm App::optex::mask
95              
96             =head1 SEE ALSO
97              
98             =over 2
99              
100             =item *
101              
102             L
103              
104             =item *
105              
106             L
107              
108             =item *
109              
110             L
111              
112             =item *
113              
114             L
115              
116             =back
117              
118             =head1 AUTHOR
119              
120             Kazumasa Utashiro
121              
122             =head1 LICENSE
123              
124             Copyright ©︎ 2024 Kazumasa Utashiro
125              
126             This library is free software; you can redistribute it and/or modify
127             it under the same terms as Perl itself.
128              
129             =cut
130              
131 10     10   68 use List::Util qw(first);
  10         65  
  10         810  
132 10     10   789 use Hash::Util qw(lock_keys);
  10         4443  
  10         92  
133 10     10   1434 use Data::Dumper;
  10         10086  
  10         8107  
134              
135             our @mask_pattern;
136             my @restore_list;
137              
138             my %option = (
139             mode => 'xml',
140             encode => 1,
141             decode => 1,
142             start => 1,
143             debug => undef,
144             );
145             lock_keys(%option);
146              
147             my($mod, $argv);
148              
149             sub initialize {
150 9     9 0 11019 ($mod, $argv) = @_;
151 9 50   18   90 if (defined (my $i = first { $argv->[$_] eq '--' } keys @$argv)) {
  18         84  
152 9         42 @mask_pattern = splice @$argv, 0, $i;
153 9 50       54 shift @$argv eq '--' or die;
154             }
155             }
156              
157             sub debug {
158 20 50   20 1 83 $option{debug} or return;
159 0   0     0 my $mark = shift // 'debug';
160 0 0       0 local *_ = @_ ? \$_[0] : \$_;
161 0         0 warn s/^/[$mark] /mgr;
162             }
163              
164             my %newtag = (
165             xml => sub {
166             my $s = shift;
167             state $id = $option{start};
168             sprintf "", $id++;
169             },
170             );
171              
172             sub newtag {
173             state $f = $newtag{$option{mode}}
174 27 50   27 0 99 or die "$option{mode}: unknown mode.\n";
175 27         63 $f->(@_);
176             }
177              
178             sub mask {
179 9     9 0 753975 my %arg = @_;
180 9         27 my $mode = $arg{mode};
181 9   50     21 local $_ = do { local $/; <> } // die $!;
  9         36  
  9         444  
182 9 50       54 $option{encode} or return $_;
183 9         21 my $id = 0;
184 9         39 debug 1;
185 9         27 for my $pat (@mask_pattern) {
186 9         243 s{$pat}{
187 27         78 my $tag = newtag(${^MATCH});
188 27         129 push @restore_list, $tag, ${^MATCH};
189 27         231 $tag;
190             }gpe;
191             }
192 9         66 debug 2;
193 9         48 return $_;
194             }
195              
196             sub unmask {
197 3     3 0 25094 my %arg = @_;
198 3         418 my $mode = $arg{mode};
199 3   50     54 local $_ = do { local $/; <> } // die $!;
  3         299  
  3         1801794  
200 3 100       52 $option{decode} or do { print $_; return };
  2         84  
  2         27  
201 1         23 my @restore = @restore_list;
202 1         24 debug 3;
203 1         15 while (my($str, $replacement) = splice @restore, 0, 2) {
204 3         215 s/\Q$str/$replacement/g;
205             }
206 10     10   86 use Encode ();
  10         20  
  10         1930  
207 1 50       16 $_ = Encode::decode('utf8', $_) if not utf8::is_utf8($_);
208 1         5 debug 4;
209 1         35 print $_;
210             }
211              
212             sub set {
213 6     6 0 2958 while (my($k, $v) = splice(@_, 0, 2)) {
214 9 50       39 exists $option{$k} or next;
215 9         45 $option{$k} = $v;
216             }
217 6         48 ();
218             }
219              
220             1;
221              
222             __DATA__