File Coverage

blib/lib/App/week/CalYear.pm
Criterion Covered Total %
statement 43 157 27.3
branch 0 42 0.0
condition 0 17 0.0
subroutine 15 28 53.5
pod 0 11 0.0
total 58 255 22.7


line stmt bran cond sub pod time code
1             package App::week::CalYear;
2              
3 1     1   12 use v5.14;
  1         3  
4 1     1   6 use warnings;
  1         2  
  1         28  
5 1     1   5 use utf8;
  1         2  
  1         5  
6              
7 1     1   29 use Exporter 'import';
  1         3  
  1         48  
8             our @EXPORT_OK = qw(@calyear);
9              
10 1     1   6 use Encode;
  1         2  
  1         81  
11 1     1   5 use Data::Dumper;
  1         2  
  1         49  
12 1     1   6 use open IO => ':utf8';
  1         1  
  1         10  
13 1     1   164 use List::Util qw(uniq);
  1         2  
  1         55  
14 1     1   6 use Hash::Util qw(lock_keys);
  1         2  
  1         8  
15 1     1   84 use Text::VisualWidth::PP qw(vwidth);
  1         3  
  1         47  
16 1     1   6 use Text::ANSI::Fold;
  1         2  
  1         59  
17 1     1   12 use Date::Japanese::Era;
  1         2  
  1         6  
18              
19             tie our @calyear, __PACKAGE__;
20              
21             sub TIEARRAY {
22 1     1   2 my $pkg = shift;
23 1         4 bless {}, $pkg;
24             }
25              
26             sub FETCH {
27 0     0     my($obj, $year) = @_;
28 0   0       $obj->{$year} //= [ CalYear($year) ];
29             }
30              
31             my %config = (
32             show_year => 1,
33             overstruck => 1,
34             wareki => undef,
35             netbsd => undef,
36             crashspace => undef,
37             tabify => undef,
38             shortmonth => undef,
39             weeknumber => 0, # 0)none 1)us 2)standard 3)iso
40             );
41             lock_keys %config;
42              
43             sub Configure {
44 0     0 0   while (my($k, $v) = splice(@_, 0, 2)) {
45 0           $config{$k} = $v;
46             }
47             }
48              
49             sub CalYear {
50 0     0 0   my $year = sprintf "%4d", shift;
51             my $cal = normalize(
52 0 0         $config{weeknumber} > 1 ? gcal($year) : cal($year)
53             );
54 0           my @cal = split /\n/, $cal, -1;
55 0           my @monthline = do {
56 0           map { $_ - 2 } # 2 lines up
57 0           grep { $cal[$_] =~ /\s 1 \s/x } # find 1st day
  0            
58             0 .. $#cal;
59             };
60 0 0         @monthline == 4 or die "cal(1) command format error.\n";
61              
62 0           state $fielder = do {
63 0           my @weekline = map $_ + 1, @monthline;
64 0           fielder($cal[ $weekline[0] ]);
65             };
66              
67 0           my @month = ( [ $cal[0] ], map [], 1..12 );
68 0           for my $i (0 .. $#monthline) {
69 0           my $start = $monthline[$i];
70 0           for my $n (0..7) {
71 0           my @m = $fielder->($cal[$start + $n]);
72 0           push @{$month[$i * 3 + 1]}, $m[0];
  0            
73 0           push @{$month[$i * 3 + 2]}, $m[1];
  0            
74 0           push @{$month[$i * 3 + 3]}, $m[2];
  0            
75             }
76             }
77              
78 0 0         insert_week_number(@month[1..12]) if $config{weeknumber} == 1;
79 0           tidy_up(@month[1..12]);
80              
81 0   0       my $wareki = $config{wareki} // $month[1][1] =~ /火/;
82 0           for my $month (&show_year($year)) {
83 0 0 0       1 <= $month and $month <= 12 or next;
84 0           insert_year(\$month[$month][0], $year, $month, $wareki);
85             }
86 0           @month;
87             }
88              
89             sub normalize {
90 0     0 0   local $_ = shift;
91 0 0         if (/\t/) { $_ = expand_tab($_) }
  0            
92 0 0         if (/\cH/) { s/.\cH//g }
  0            
93 0           $_;
94             }
95              
96             sub cal {
97 0     0 0   my $option = shift;
98 0           local $_ = `cal $option`;
99 0 0         if ($config{crashspace}) {
100 0           s/ +$//mg;
101             }
102 0 0         if ($config{netbsd}) {
103 0           s/(Su|Mo|We|Fr|Sa)/sprintf '%2.1s', $1/mge;
  0            
104             }
105 0 0         if ($config{shortmonth}) {
106 0           s{([A-Z][a-z][a-z])(\w+ )}{
107 1     1   1221 use integer;
  1         2  
  1         16  
108 0           my $sp = length($2);
109 0           (' ' x ($sp/2 + $sp%2)) . $1 . (' ' x ($sp/2));
110             }mge;
111             }
112 0 0 0       if ($config{tabify} and !/\t/) {
113             # does not expect wide characters
114 0           s{(.{8})}{ $1 =~ s/ +$/\t/r }ge;
  0            
115             }
116 0           $_;
117             }
118              
119             sub gcal {
120 0     0 0   my $option = shift;
121 0 0         my $iso = '--iso-week-number=' . ($config{weeknumber} == 2 ? 'no' : 'yes');
122 0           my $exec = "gcal -i -H no $iso -K $option";
123 0           local $_ = qx/$exec/;
124 0           $_;
125             }
126              
127             sub insert_week_number {
128 0     0 0   my $n = 1;
129 0           for my $month (@_) {
130 0           $month->[0] .= ' ';
131 0           $month->[1] .= ' CW';
132 0           for (@{$month}[2..7]) {
  0            
133 0 0         my $cw = /\S/ ? sprintf(' %02d', $n) : ' ';
134 0 0         $n++ if /\S$/;
135 0           $_ .= $cw;
136             }
137             }
138             }
139              
140             sub tidy_up {
141 0     0 0   for my $month (@_) {
142             # insert frame
143 0           $_ = " $_ " for @$month;
144             # fix month name:
145 0           for ($month->[0]) {
146             # 1) Take care of cal(1) multibyte string bug.
147             # 2) Normalize off-to-right to off-to-left.
148 0 0         if (/^( +)(\S+)( +)$/) {
149 0           my $sp = length $1.$3;
150 0           my $left = int $sp / 2;
151 0           my $right = $left + $sp % 2;
152 0           $_ = ' ' x $left . $2 . ' ' x $right;
153             }
154             }
155             }
156             }
157              
158             sub fielder {
159 0     0 0   my $dow_line = shift;
160 1     1   631 use Unicode::EastAsianWidth;
  1         3  
  1         105  
161 0           my $dow_re = qr/\p{InFullwidth}|[ \S]\S/;
162 0 0         $dow_line =~ m{^ (\s*)
163             ( (?: $dow_re [ ]){6} $dow_re (?:[ ]CW)? ) (\s+)
164             ( (?: $dow_re [ ]){6} $dow_re (?:[ ]CW)? ) (\s+)
165             ( (?: $dow_re [ ]){6} $dow_re (?:[ ]CW)? )
166             }x or die "cal(1): unexpected day-of-week line.";
167 0           my $w = vwidth $2;
168 0           my @w = (length $1, $w, length $3, $w, length $5, $w);
169 0           my $blank = ' ' x $w;
170 0           my $fold = Text::ANSI::Fold->new(width => \@w, padding => 1);
171             sub {
172 0     0     my $l = shift;
173 0           my @f = $fold->text($l)->chops;
174 0   0       map { $_ // $blank } @f[1, 3, 5];
  0            
175 0           };
176             }
177              
178             sub show_year {
179 0     0 0   my $conf = $config{show_year};
180 0           my $year = shift;
181 0 0         if ((my $ref = ref $conf) eq '') {
    0          
    0          
182 0           ( $conf );
183             }
184             elsif ($ref eq 'ARRAY') {
185 0           @{$conf};
  0            
186             }
187             elsif ($ref eq 'HASH') {
188 0           uniq do {
189             map {
190 0           my $v = $conf->{$_};
191 0 0         ref $v eq 'ARRAY' ? @$v : $v
192             }
193 0 0         grep { $_ eq '*' or $_ == $year }
  0            
194             keys %$conf;
195             };
196             }
197             }
198              
199             sub insert_year {
200 0     0 0   local *_ = shift;
201 0           my($year, $month, $wareki) = @_;
202 0           my $len = length($year);
203 0           s/^[ ]\K[ ]{$len}/$year/;
204 0 0 0       if (1873 <= $year and $wareki) {
205 0           my $era = Date::Japanese::Era->new($year, $month, 1);
206 0           $year = sprintf '%s%d', $era->name, $era->year;
207 0           $len = vwidth $year;
208             }
209 0           s/[ ]{$len}(?=[ ]$)/$year/;
210             }
211              
212             sub expand_tab {
213 0     0 0   local $_ = shift;
214 0           my $ts = 8;
215 0           s{ (?:^|\G) (?.*?) \K (?\t+) }{
216 0           my $w = vwidth($+{lead});
217 0           (' ' x ($ts * length($+{tab}) - ($w % $ts)));
218             }xgme;
219 0           $_;
220             }
221              
222             1;