File Coverage

blib/lib/Bot/Cobalt/Utils.pm
Criterion Covered Total %
statement 133 147 90.4
branch 59 86 68.6
condition 5 8 62.5
subroutine 18 18 100.0
pod 11 11 100.0
total 226 270 83.7


line stmt bran cond sub pod time code
1             package Bot::Cobalt::Utils;
2             $Bot::Cobalt::Utils::VERSION = '0.021003';
3 35     35   67805 use strictures 2;
  35         5620  
  35         1079  
4 35     35   4709 use Carp;
  35         109  
  35         1883  
5 35     35   136 use Scalar::Util 'reftype';
  35         78  
  35         1910  
6              
7 35     35   14269 use App::bmkpasswd ();
  35         754571  
  35         819  
8              
9 35     35   212 use parent 'Exporter::Tiny';
  35         44  
  35         187  
10              
11             our @EXPORT_OK = qw/
12             secs_to_str
13             secs_to_str_y
14             secs_to_timestr
15             timestr_to_secs
16              
17             mkpasswd
18             passwdcmp
19              
20             color
21              
22             glob_grep
23             glob_to_re
24             glob_to_re_str
25             rplprintf
26             /;
27              
28             our %EXPORT_TAGS = (
29             ALL => [ @EXPORT_OK ],
30             );
31              
32              
33             ## codes mostly borrowed from IRC::Utils
34             our %COLORS = (
35             NORMAL => "\x0f",
36              
37             BOLD => "\x02",
38             UNDERLINE => "\x1f",
39             REVERSE => "\x16",
40             ITALIC => "\x1d",
41              
42             WHITE => "\x0300",
43             BLACK => "\x0301",
44             BLUE => "\x0302",
45             GREEN => "\x0303",
46             RED => "\x0304",
47             BROWN => "\x0305",
48             PURPLE => "\x0306",
49             ORANGE => "\x0307",
50             YELLOW => "\x0308",
51             TEAL => "\x0310",
52             PINK => "\x0313",
53             GREY => "\x0314",
54             GRAY => "\x0314",
55              
56             LIGHT_BLUE => "\x0312",
57             LIGHT_CYAN => "\x0311",
58             LIGHT_GREEN => "\x0309",
59             LIGHT_GRAY => "\x0315",
60             LIGHT_GREY => "\x0315",
61             );
62              
63             my %default_fmt_vars;
64             for my $color (keys %COLORS) {
65             my $fmtvar = 'C_'.$color;
66             $default_fmt_vars{$fmtvar} = $COLORS{$color};
67             }
68              
69             ## String formatting, usually for langsets:
70             sub rplprintf {
71 9     9 1 1427 my $string = shift;
72 9 50       21 return '' unless $string;
73              
74             ## rplprintf( $string, $vars )
75             ## returns empty string if no string is specified.
76             ##
77             ## variables can be terminated with % or a space:
78             ## rplprintf( "Error for %user%: %err")
79             ##
80             ## used for formatting lang RPLs
81             ## $vars should be a hash keyed by variable, f.ex:
82             ## 'user' => $username,
83             ## 'err' => $error,
84              
85 9         8 my %vars;
86              
87 9 100       19 if (@_ > 1) {
88 1         2 my %args = @_;
89 1         8 %vars = ( %default_fmt_vars, %args );
90             } else {
91 8 50       33 if (reftype $_[0] eq 'HASH') {
92 8         30 %vars = ( %default_fmt_vars, %{$_[0]} );
  8         78  
93             } else {
94 0         0 confess "rplprintf() expects a hash"
95             }
96             }
97              
98             my $repl = sub {
99             ## _repl($1, $2, $vars)
100 34     34   49 my ($orig, $match) = @_;
101 34 100       103 defined $vars{$match} ? $vars{$match} : $orig
102 9         42 };
103              
104 9         27 my $regex = qr/(%([^\s%]+)%?)/;
105              
106 9         57 $string =~ s/$regex/$repl->($1, $2)/ge;
  34         40  
107              
108 9         81 $string
109             }
110              
111              
112             ## Glob -> regex functions:
113              
114             sub glob_grep ($;@) {
115 4     4 1 5 my $glob = shift;
116 4 50       10 confess "glob_grep called with no arguments!"
117             unless defined $glob;
118              
119 4 100       11 my @array = ref $_[0] eq 'ARRAY' ? @{$_[0]} : @_ ;
  2         3  
120              
121 4         8 my $re = glob_to_re($glob);
122              
123 4         6 grep { m/$re/ } @array
  8         38  
124             }
125              
126             sub glob_to_re ($) {
127 6     6 1 17 my ($glob) = @_;
128 6 50       13 confess "glob_to_re called with no arguments!"
129             unless defined $glob;
130              
131 6         7 my $re = glob_to_re_str($glob);
132              
133 6         78 qr/$re/
134             }
135              
136             sub glob_to_re_str ($) {
137             ## Currently allows:
138             ## * == .*
139             ## ? == .
140             ## + == literal space
141             ## leading ^ (beginning of str) is accepted
142             ## so is trailing $
143             ## char classes are accepted
144 8     8 1 9 my ($glob) = @_;
145 8 50       13 confess "glob_to_re_str called with no arguments!"
146             unless defined $glob;
147              
148 8         7 my($re, $in_esc);
149 8         7 my ($first, $pos) = (1, 0);
150 8         27 my @chars = split '', $glob;
151 8         13 for my $ch (@chars) {
152 82         41 ++$pos;
153 82 100       103 my $last = 1 if $pos == @chars;
154              
155             ## Leading ^ (start) is OK:
156 82 100 100     172 if ($first) {
    100          
157 11 100       17 if ($ch eq '^') {
158 3         5 $re .= '^' ;
159             next
160 3         3 }
161 8         7 $first = 0;
162             ## So is trailing $ (end):
163             } elsif ($last && $ch eq '$') {
164 3         3 $re .= '$' ;
165             last
166 3         3 }
167              
168             ## Escape metas:
169 76 50       59 if (grep { $_ eq $ch } qw/ . ( ) | ^ $ @ % { } /) {
  760         619  
170 0         0 $re .= "\\$ch" ;
171 0         0 $in_esc = 0;
172             next
173 0         0 }
174              
175             ## Handle * ? + wildcards:
176 76 100       85 if ($ch eq '*') {
177 6 50       8 $re .= $in_esc ? '\*' : '.*' ;
178 6         4 $in_esc = 0;
179             next
180 6         7 }
181 70 100       75 if ($ch eq '?') {
182 2 50       5 $re .= $in_esc ? '\?' : '.' ;
183 2         3 $in_esc = 0;
184             next
185 2         3 }
186 68 100       73 if ($ch eq '+') {
187 3 50       4 $re .= $in_esc ? '\+' : '\s' ;
188 3         3 $in_esc = 0;
189             next
190 3         2 }
191 65 50 33     159 if ( $ch eq '[' || $ch eq ']' ) {
192 0 0       0 $re .= $in_esc ? "\\$ch" : $ch ;
193 0         0 $in_esc = 0;
194             next
195 0         0 }
196              
197             ## Switch on/off escaping:
198 65 50       70 if ($ch eq "\\") {
199 0 0       0 if ($in_esc) {
200 0         0 $re .= "\\\\";
201 0         0 $in_esc = 0;
202 0         0 } else { $in_esc = 1; }
203             next
204 0         0 }
205              
206 65         48 $re .= $ch;
207 65         60 $in_esc = 0;
208             }
209              
210             $re
211 8         21 }
212              
213              
214             ## IRC color codes:
215             sub color ($;$) {
216             ## color($format, $str)
217             ## implements mirc formatting codes, against my better judgement
218             ## if format is unspecified, returns NORMAL
219              
220             ## interpolate bold, reset to NORMAL after:
221             ## $str = color('bold') . "Text" . color;
222             ## -or-
223             ## format specified strings, resetting NORMAL after:
224             ## $str = color('bold', "Some text"); # bold text ending in normal
225              
226 4     4 1 346 my ($format, $str) = @_;
227 4   50     8 $format = uc($format||'normal');
228              
229 4         8 my $selected = $COLORS{$format};
230              
231 4 100       170 carp "Invalid COLOR $format passed to color()"
232             unless $selected;
233              
234 4 50       12 return $selected . $str . $COLORS{NORMAL} if $str;
235 4 100       20 $selected || $COLORS{NORMAL};
236             }
237              
238              
239             ## Time/date ops:
240             sub timestr_to_secs ($) {
241             ## turn something like 2h3m30s into seconds
242 4     4 1 10 my ($timestr) = @_;
243              
244 4 50       8 unless ($timestr) {
245 0         0 carp "timestr_to_secs() received a false value";
246 0         0 return 0
247             }
248              
249             ## maybe just seconds:
250 4 50       14 return $timestr if $timestr =~ /^[0-9]+$/;
251              
252 4         16 my @chunks = $timestr =~ m/([0-9]+)([dhms])/gc;
253              
254 4         5 my $secs = 0;
255 4         14 while ( my ($ti, $unit) = splice @chunks, 0, 2 ) {
256             UNIT: {
257 6 100       4 if ($unit eq 'd') {
  6         11  
258 1         2 $secs += $ti * 86400;
259             last UNIT
260 1         3 }
261              
262 5 100       7 if ($unit eq 'h') {
263 1         2 $secs += $ti * 3600;
264             last UNIT
265 1         3 }
266              
267 4 100       6 if ($unit eq 'm') {
268 2         3 $secs += $ti * 60;
269             last UNIT
270 2         6 }
271              
272 2         6 $secs += $ti;
273             }
274             }
275              
276             $secs
277 4         15 }
278              
279             sub _time_breakdown ($) {
280 8     8   6 my ($diff) = @_;
281 8 50       11 return unless defined $diff;
282              
283 8         15 my $days = int $diff / 86400;
284 8         6 my $sec = $diff % 86400;
285 8         6 my $hours = int $sec / 3600;
286 8         8 $sec %= 3600;
287 8         5 my $mins = int $sec / 60;
288 8         6 $sec %= 60;
289              
290 8         14 ($days, $hours, $mins, $sec)
291             }
292              
293             sub secs_to_timestr ($) {
294 3     3 1 4 my ($diff) = @_;
295 3 50       7 return unless defined $diff;
296 3         5 my ($days, $hours, $mins, $sec) = _time_breakdown($diff);
297              
298 3         16 my $str;
299 3 50       6 $str .= $days .'d' if $days;
300 3 50       5 $str .= $hours .'h' if $hours;
301 3 50       8 $str .= $mins .'m' if $mins;
302 3 100       4 $str .= $sec .'s' if $sec;
303              
304 3         11 $str
305             }
306              
307             sub secs_to_str ($) {
308             ## turn seconds into a string like '0 days, 00:00:00'
309 3     3 1 5 my ($diff) = @_;
310 3 50       6 return unless defined $diff;
311 3         5 my ($days, $hours, $mins, $sec) = _time_breakdown($diff);
312 3 100       7 my $plural = $days == 1 ? 'day' : 'days';
313 3         16 sprintf "%d $plural, %2.2d:%2.2d:%2.2d", $days, $hours, $mins, $sec
314             }
315              
316             sub secs_to_str_y {
317 2     2 1 3 my ($diff) = @_;
318 2 50       5 return unless defined $diff;
319 2         3 my ($days, $hrs, $mins, $sec) = _time_breakdown($diff);
320 2         5 my $yrs = int $days / 365;
321 2         1 $days %= 365;
322 2 100       7 my $plural_y = $yrs > 1 ? 'years' : 'year';
323 2 50       4 my $plural_d = $days == 1 ? 'day' : 'days';
324 2 50       13 $yrs ?
325             sprintf "%d $plural_y, %d $plural_d, %2.2d:%2.2d:%2.2d",
326             $yrs, $days, $hrs, $mins, $sec
327             : sprintf "%d $plural_d, %2.2d:%2.2d:%2.2d",
328             $days, $hrs, $mins, $sec
329             }
330              
331              
332             ## App::bmkpasswd stubs as of 00_35
333 1     1 1 53 sub mkpasswd ($;@) { App::bmkpasswd::mkpasswd(@_) }
334 2     2 1 47371 sub passwdcmp ($$) { App::bmkpasswd::passwdcmp(@_) }
335              
336             1;
337             __END__