File Coverage

blib/lib/App/week/CalYear.pm
Criterion Covered Total %
statement 46 167 27.5
branch 0 46 0.0
condition 0 17 0.0
subroutine 16 29 55.1
pod 0 11 0.0
total 62 270 22.9


line stmt bran cond sub pod time code
1             package App::week::CalYear;
2              
3 4     4   55 use v5.24;
  4         30  
4 4     4   22 use warnings;
  4         9  
  4         242  
5 4     4   19 use utf8;
  4         12  
  4         43  
6              
7 4     4   128 use Exporter 'import';
  4         11  
  4         249  
8             our @EXPORT_OK = qw(@calyear);
9              
10 4     4   23 use Encode;
  4         7  
  4         385  
11 4     4   21 use Data::Dumper;
  4         13  
  4         227  
12 4     4   16 use open IO => ':utf8';
  4         29  
  4         32  
13 4     4   346 use List::Util qw(uniq);
  4         7  
  4         330  
14 4     4   24 use Hash::Util qw(lock_keys);
  4         7  
  4         35  
15 4     4   247 use Text::VisualWidth::PP qw(vwidth);
  4         7  
  4         222  
16 4     4   32 use Text::ANSI::Fold;
  4         5  
  4         134  
17 4     4   16 use Date::Japanese::Era;
  4         8  
  4         28  
18              
19             tie our @calyear, __PACKAGE__;
20              
21             sub TIEARRAY {
22 4     4   7 my $pkg = shift;
23 4         16 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             stripe => 0,
41             );
42             lock_keys %config;
43              
44             sub Configure {
45 0     0 0   while (my($k, $v) = splice(@_, 0, 2)) {
46 0           $config{$k} = $v;
47             }
48             }
49              
50             sub CalYear {
51 0     0 0   my $year = sprintf "%4d", shift;
52             my $cal = normalize(
53 0 0         $config{weeknumber} > 1 ? gcal($year) : cal($year)
54             );
55 0           my @cal = split /\n/, $cal, -1;
56 0           my @monthline = do {
57 0           map { $_ - 2 } # 2 lines up
58 0           grep { $cal[$_] =~ /\s 1 \s/x } # find 1st day
  0            
59             keys @cal;
60             };
61 0 0         @monthline == 4 or die "cal(1) command format error.\n";
62              
63 0           state $fielder = do {
64 0           my @weekline = map $_ + 1, @monthline;
65 0           fielder($cal[ $weekline[0] ]);
66             };
67              
68 0           my @month = ( [ $cal[0] ], map [], 1..12 );
69 0           while (my($i, $start) = each @monthline) {
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 4     4   7814 use integer;
  4         7  
  4         31  
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 4     4   1951 use Unicode::EastAsianWidth;
  4         8  
  4         1095  
141              
142             sub tidy_up {
143 0     0 0   for my $month (@_) {
144             # insert frame
145 0           $_ = " $_ " for @{$month}[0..1];
  0            
146 0           for (@{$month}[2..$#{$month}]) {
  0            
  0            
147 0           my $c = ' ';
148 0 0         if (my $stripe = $config{stripe}) {
149 0 0         $c = $stripe =~ /\D/ ? $stripe : '│';
150             }
151 0           $_ = $c . s/(\p{InFullwidth}|..)( )/$1$c/gr . $c;
152             }
153             # fix month name:
154 0           for ($month->[0]) {
155             # 1) Take care of cal(1) multibyte string bug.
156             # 2) Normalize off-to-right to off-to-left.
157 0 0         if (/^( +)(\S+)( +)$/) {
158 0           my $sp = length $1.$3;
159 0           my $left = int $sp / 2;
160 0           my $right = $left + $sp % 2;
161 0           $_ = ' ' x $left . $2 . ' ' x $right;
162             }
163             }
164             }
165             }
166              
167             sub fielder {
168 0     0 0   my $dow_line = shift;
169 4     4   2168 use Unicode::EastAsianWidth;
  4         11  
  4         3440  
170 0           my $dow_re = qr/\p{InFullwidth}|[ \S]\S/;
171 0 0         $dow_line =~ m{^ (\s*)
172             ( (?: $dow_re [ ]){6} $dow_re (?:[ ]CW)? ) (\s+)
173             ( (?: $dow_re [ ]){6} $dow_re (?:[ ]CW)? ) (\s+)
174             ( (?: $dow_re [ ]){6} $dow_re (?:[ ]CW)? )
175             }x or die "cal(1): unexpected day-of-week line.";
176 0           my $w = vwidth $2;
177 0           my @w = (length $1, $w, length $3, $w, length $5, $w);
178 0           my $blank = ' ' x $w;
179 0           my $fold = Text::ANSI::Fold->new(width => \@w, padding => 1);
180             sub {
181 0     0     my $l = shift;
182 0           my @f = $fold->text($l)->chops;
183 0   0       map { $_ // $blank } @f[1, 3, 5];
  0            
184 0           };
185             }
186              
187             sub show_year {
188 0     0 0   my $conf = $config{show_year};
189 0           my $year = shift;
190 0 0         if ((my $ref = ref $conf) eq '') {
    0          
    0          
191 0           ( $conf );
192             }
193             elsif ($ref eq 'ARRAY') {
194 0           @{$conf};
  0            
195             }
196             elsif ($ref eq 'HASH') {
197 0           uniq do {
198             map {
199 0           my $v = $conf->{$_};
200 0 0         ref $v eq 'ARRAY' ? @$v : $v
201             }
202 0 0         grep { $_ eq '*' or $_ == $year }
  0            
203             keys %$conf;
204             };
205             }
206             }
207              
208             sub insert_year {
209 0     0 0   local *_ = shift;
210 0           my($year, $month, $wareki) = @_;
211 0           my $len = length($year);
212 0           s/^[ ]\K[ ]{$len}/$year/;
213 0 0 0       if (1873 <= $year and $wareki) {
214 0           my $era = Date::Japanese::Era->new($year, $month, 1);
215 0           $year = sprintf '%s%d', $era->name, $era->year;
216 0           $len = vwidth $year;
217             }
218 0           s/[ ]{$len}(?=[ ]$)/$year/;
219             }
220              
221             sub expand_tab {
222 0     0 0   local $_ = shift;
223 0           my $ts = 8;
224 0           s{ (?:^|\G) (?.*?) \K (?\t+) }{
225 0           my $w = vwidth($+{lead});
226 0           (' ' x ($ts * length($+{tab}) - ($w % $ts)));
227             }xgme;
228 0           $_;
229             }
230              
231             1;