File Coverage

blib/lib/Term/ProgressSpinner.pm
Criterion Covered Total %
statement 208 330 63.0
branch 60 152 39.4
condition 17 34 50.0
subroutine 38 51 74.5
pod 41 43 95.3
total 364 610 59.6


line stmt bran cond sub pod time code
1             package Term::ProgressSpinner;
2             our $VERSION = '1.00';
3 3     3   217663 use 5.006; use strict; use warnings;
  3     3   34  
  3     3   17  
  3         7  
  3         84  
  3         17  
  3         4  
  3         96  
4 3     3   1815 use IO::Handle; use Term::ANSIColor; use Time::HiRes qw//;
  3     3   19741  
  3     3   178  
  3         2275  
  3         27952  
  3         342  
  3         2183  
  3         4635  
  3         112  
5 3     3   1543 use Term::Size::Any qw/chars/;
  3         835  
  3         18  
6             if ($^O eq 'MSWin32') { eval "use Win32::Console::ANSI; use Win32::Console;"; }
7             our (%SPINNERS, %PROGRESS, %VALIDATE);
8              
9             BEGIN {
10             %VALIDATE = (
11             colours => {
12             map {
13 3     3   9545 my $c = $_;
  24         44  
14             (
15             $c => 1,
16             "bright_${c}" => 1,
17 24         47 (map { (
18 192         1444 "${c} on_${_}" => 1,
19             "${c} on_bright_${_}" => 1,
20             "bright_${c} on_${_}" => 1,
21             "bright_${c} on_bright_${_}" => 1,
22             ) } qw/black red green yellow blue magenta cyan white/)
23             )
24             } qw/black red green yellow blue magenta cyan white/
25             },
26             msg_regex => qr/\{(total|progress|spinner|percents|percentage|percentages|percent|counter|elapsed|elapsed_second|estimate|estimate_second|start_epoch|start_epoch_second|epoch|epoch_second|per_second|last_advance_epoch|last_advance_epoch_second|last_elapsed|last_elapsed_second|elapsed|elapsed_second)\}/
27             );
28 3         211 %SPINNERS = (
29             bar => {
30             width => 3,
31             index => [4, 2, 6],
32             chars => [
33             "▁",
34             "▂",
35             "▃",
36             "▄",
37             "▅",
38             "▆",
39             "▇",
40             "█"
41             ]
42             },
43             dots => {
44             width => 1,
45             index => [1],
46             chars => [
47             "⠋",
48             "⠙",
49             "⠹",
50             "⠸",
51             "⠼",
52             "⠴",
53             "⠦",
54             "⠧",
55             "⠇",
56             "⠏"
57             ]
58             },
59             around => {
60             width => 1,
61             index => [1],
62             chars => [
63             "⢀⠀",
64             "⡀⠀",
65             "⠄⠀",
66             "⢂⠀",
67             "⡂⠀",
68             "⠅⠀",
69             "⢃⠀",
70             "⡃⠀",
71             "⠍⠀",
72             "⢋⠀",
73             "⡋⠀",
74             "⠍⠁",
75             "⢋⠁",
76             "⡋⠁",
77             "⠍⠉",
78             "⠋⠉",
79             "⠋⠉",
80             "⠉⠙",
81             "⠉⠙",
82             "⠉⠩",
83             "⠈⢙",
84             "⠈⡙",
85             "⢈⠩",
86             "⡀⢙",
87             "⠄⡙",
88             "⢂⠩",
89             "⡂⢘",
90             "⠅⡘",
91             "⢃⠨",
92             "⡃⢐",
93             "⠍⡐",
94             "⢋⠠",
95             "⡋⢀",
96             "⠍⡁",
97             "⢋⠁",
98             "⡋⠁",
99             "⠍⠉",
100             "⠋⠉",
101             "⠋⠉",
102             "⠉⠙",
103             "⠉⠙",
104             "⠉⠩",
105             "⠈⢙",
106             "⠈⡙",
107             "⠈⠩",
108             "⠀⢙",
109             "⠀⡙",
110             "⠀⠩",
111             "⠀⢘",
112             "⠀⡘",
113             "⠀⠨",
114             "⠀⢐",
115             "⠀⡐",
116             "⠀⠠",
117             "⠀⢀",
118             "⠀⡀"
119             ]
120             },
121             pipe => {
122             width => 1,
123             index => [1],
124             chars => [
125             "┤",
126             "┘",
127             "┴",
128             "└",
129             "├",
130             "┌",
131             "┬",
132             "┐"
133             ]
134             },
135             moon => {
136             width => 1,
137             index => [1],
138             chars => [
139             "🌑 ",
140             "🌒 ",
141             "🌓 ",
142             "🌔 ",
143             "🌕 ",
144             "🌖 ",
145             "🌗 ",
146             "🌘 "
147             ]
148             },
149             circle => {
150             width => 1,
151             index => [1],
152             chars => [
153             "・",
154             "◦",
155             "●",
156             "○",
157             "◎",
158             "◉",
159             "⦿",
160             "◉",
161             "◎",
162             "○",
163             "◦",
164             "・",
165             ]
166             },
167             color_circle => {
168             width => 1,
169             index => [1],
170             chars => [
171             "🔴",
172             "🟠",
173             "🟡",
174             "🟢",
175             "🔵",
176             "🟣",
177             "⚫️",
178             "⚪️",
179             "🟤"
180             ]
181             },
182             color_circles => {
183             width => 3,
184             index => [1, 4, 7],
185             chars => [
186             "🔴",
187             "🟠",
188             "🟡",
189             "🟢",
190             "🔵",
191             "🟣",
192             "⚫️",
193             "⚪️",
194             "🟤"
195             ]
196             },
197             color_square => {
198             width => 1,
199             index => [1],
200             chars => [
201             "🟥",
202             "🟧",
203             "🟨",
204             "🟩",
205             "🟦",
206             "🟪",
207             "⬛️",
208             "⬜️",
209             "🟫"
210             ]
211             },
212             color_squares => {
213             width => 3,
214             index => [1, 3, 6],
215             chars => [
216             "🟥",
217             "🟧",
218             "🟨",
219             "🟩",
220             "🟦",
221             "🟪",
222             "⬛️",
223             "⬜️",
224             "🟫"
225             ]
226             },
227             earth => {
228             width => 1,
229             index => [1],
230             chars => [
231             "🌎",
232             "🌍",
233             "🌏"
234             ]
235             },
236             circle_half => {
237             width => 1,
238             index => [1],
239             chars => [
240             '◐',
241             '◓',
242             '◑',
243             '◒'
244             ]
245             },
246             clock => {
247             width => 1,
248             index => [1],
249             chars => [
250             "🕛 ",
251             "🕐 ",
252             "🕑 ",
253             "🕒 ",
254             "🕓 ",
255             "🕔 ",
256             "🕕 ",
257             "🕖 ",
258             "🕗 ",
259             "🕘 ",
260             "🕙 ",
261             "🕚 "
262             ]
263             },
264             pong => {
265             width => 1,
266             index => [1],
267             chars => [
268             "▐⠂ ▌",
269             "▐⠈ ▌",
270             "▐ ⠂ ▌",
271             "▐ ⠠ ▌",
272             "▐ ⡀ ▌",
273             "▐ ⠠ ▌",
274             "▐ ⠂ ▌",
275             "▐ ⠈ ▌",
276             "▐ ⠂ ▌",
277             "▐ ⠠ ▌",
278             "▐ ⡀ ▌",
279             "▐ ⠠ ▌",
280             "▐ ⠂ ▌",
281             "▐ ⠈ ▌",
282             "▐ ⠂▌",
283             "▐ ⠠▌",
284             "▐ ⡀▌",
285             "▐ ⠠ ▌",
286             "▐ ⠂ ▌",
287             "▐ ⠈ ▌",
288             "▐ ⠂ ▌",
289             "▐ ⠠ ▌",
290             "▐ ⡀ ▌",
291             "▐ ⠠ ▌",
292             "▐ ⠂ ▌",
293             "▐ ⠈ ▌",
294             "▐ ⠂ ▌",
295             "▐ ⠠ ▌",
296             "▐ ⡀ ▌",
297             "▐⠠ ▌"
298             ]
299             },
300             material => {
301             width => 1,
302             index => [1],
303             chars => [
304             "█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
305             "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
306             "███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
307             "████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
308             "██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
309             "██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
310             "███████▁▁▁▁▁▁▁▁▁▁▁▁▁",
311             "████████▁▁▁▁▁▁▁▁▁▁▁▁",
312             "█████████▁▁▁▁▁▁▁▁▁▁▁",
313             "█████████▁▁▁▁▁▁▁▁▁▁▁",
314             "██████████▁▁▁▁▁▁▁▁▁▁",
315             "███████████▁▁▁▁▁▁▁▁▁",
316             "█████████████▁▁▁▁▁▁▁",
317             "██████████████▁▁▁▁▁▁",
318             "██████████████▁▁▁▁▁▁",
319             "▁██████████████▁▁▁▁▁",
320             "▁██████████████▁▁▁▁▁",
321             "▁██████████████▁▁▁▁▁",
322             "▁▁██████████████▁▁▁▁",
323             "▁▁▁██████████████▁▁▁",
324             "▁▁▁▁█████████████▁▁▁",
325             "▁▁▁▁██████████████▁▁",
326             "▁▁▁▁██████████████▁▁",
327             "▁▁▁▁▁██████████████▁",
328             "▁▁▁▁▁██████████████▁",
329             "▁▁▁▁▁██████████████▁",
330             "▁▁▁▁▁▁██████████████",
331             "▁▁▁▁▁▁██████████████",
332             "▁▁▁▁▁▁▁█████████████",
333             "▁▁▁▁▁▁▁█████████████",
334             "▁▁▁▁▁▁▁▁████████████",
335             "▁▁▁▁▁▁▁▁████████████",
336             "▁▁▁▁▁▁▁▁▁███████████",
337             "▁▁▁▁▁▁▁▁▁███████████",
338             "▁▁▁▁▁▁▁▁▁▁██████████",
339             "▁▁▁▁▁▁▁▁▁▁██████████",
340             "▁▁▁▁▁▁▁▁▁▁▁▁████████",
341             "▁▁▁▁▁▁▁▁▁▁▁▁▁███████",
342             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████",
343             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████",
344             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████",
345             "█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████",
346             "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███",
347             "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███",
348             "███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███",
349             "████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██",
350             "█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█",
351             "█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█",
352             "██████▁▁▁▁▁▁▁▁▁▁▁▁▁█",
353             "████████▁▁▁▁▁▁▁▁▁▁▁▁",
354             "█████████▁▁▁▁▁▁▁▁▁▁▁",
355             "█████████▁▁▁▁▁▁▁▁▁▁▁",
356             "█████████▁▁▁▁▁▁▁▁▁▁▁",
357             "█████████▁▁▁▁▁▁▁▁▁▁▁",
358             "███████████▁▁▁▁▁▁▁▁▁",
359             "████████████▁▁▁▁▁▁▁▁",
360             "████████████▁▁▁▁▁▁▁▁",
361             "██████████████▁▁▁▁▁▁",
362             "██████████████▁▁▁▁▁▁",
363             "▁██████████████▁▁▁▁▁",
364             "▁██████████████▁▁▁▁▁",
365             "▁▁▁█████████████▁▁▁▁",
366             "▁▁▁▁▁████████████▁▁▁",
367             "▁▁▁▁▁████████████▁▁▁",
368             "▁▁▁▁▁▁███████████▁▁▁",
369             "▁▁▁▁▁▁▁▁█████████▁▁▁",
370             "▁▁▁▁▁▁▁▁█████████▁▁▁",
371             "▁▁▁▁▁▁▁▁▁█████████▁▁",
372             "▁▁▁▁▁▁▁▁▁█████████▁▁",
373             "▁▁▁▁▁▁▁▁▁▁█████████▁",
374             "▁▁▁▁▁▁▁▁▁▁▁████████▁",
375             "▁▁▁▁▁▁▁▁▁▁▁████████▁",
376             "▁▁▁▁▁▁▁▁▁▁▁▁███████▁",
377             "▁▁▁▁▁▁▁▁▁▁▁▁███████▁",
378             "▁▁▁▁▁▁▁▁▁▁▁▁▁███████",
379             "▁▁▁▁▁▁▁▁▁▁▁▁▁███████",
380             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████",
381             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████",
382             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████",
383             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████",
384             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███",
385             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███",
386             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██",
387             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██",
388             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██",
389             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█",
390             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█",
391             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█",
392             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
393             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
394             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
395             "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁"
396              
397             ]
398              
399             }
400             );
401 3         17 $SPINNERS{default} = $SPINNERS{bar};
402 3         130 %PROGRESS = (
403             bar => {
404             chars => ['│', "█", '│']
405             },
406             equal => {
407             chars => ['[', "=", ']']
408             },
409             arrow => {
410             chars => ['│', "→", '│']
411             },
412             boxed_arrow => {
413             chars => ['│', "⍈", '│']
414             },
415             lines => {
416             chars => ['│', "≡", '│']
417             },
418             horizontal_lines => {
419             chars => ['│', "▤", '│']
420             },
421             vertical_lines => {
422             chars => ['│', "▥", '│']
423             },
424             hash => {
425             chars => ['[', "#", ']']
426             },
427             triangle => {
428             chars => ['│', '▶︎', '│' ]
429             },
430             den_triangle => {
431             chars => ['│', '⏅', '│' ]
432             },
433             circle => {
434             chars => ['│', 'Ⓞ', '│' ]
435             },
436             den_circle => {
437             chars => ['│', '⏂', '│' ]
438             },
439             shekel => {
440             chars => ['│', '₪', '│' ]
441             },
442             dots => {
443             chars => ['│', '▒', '│' ]
444             },
445             square => {
446             chars => ['│', '■', '│' ]
447             },
448             block => {
449             chars => ["【", "=", "】"]
450             }
451             );
452 3         12904 $PROGRESS{default} = $PROGRESS{bar};
453             }
454              
455             sub new {
456 25 50   25 1 743 my ($pkg, %args) = (shift, ref $_[0] ? %{$_[0]} : @_);
  0         0  
457             $args{$_} and ($VALIDATE{colours}{$args{$_}} or die "Invalid color for $_")
458 25   50     781 for qw/text_color total_color counter_color percent_color percentage_color percentages_color percents_color spinner_color progress_color elapsed_color last_elapsed_color estimate_color last_advance_epoch_color start_epoch_color epoch_color/;
      66        
459 25   100     93 $args{precision} ||= 3;
460             return bless {
461             text_color => 'white',
462             total_color => 'white',
463             counter_color => 'white',
464             percent_color => 'white',
465             percentage_color => 'white',
466             percentages_color => 'white',
467             percents_color => 'white',
468             spinner_color => 'white',
469             elapsed_color => 'white',
470             start_epoch_color => 'white',
471             last_elapsed_color => 'white',
472             last_advance_epoch_color => 'white',
473             estimate_color => 'white',
474             epoch_color => 'white',
475             per_second_color => 'white',
476             spinner_options => $SPINNERS{ $args{spinner} || 'default' },
477             progress_color => 'white',
478             progress_width => 20,
479 25   50     1165 progress_options => $PROGRESS{ $args{progress} || 'default' },
      50        
      66        
480             output => \*STDERR,
481             progress_spinner_index => 0,
482             progress_spinners => [],
483             message => "{progress} {spinner} processed {percents} of {counter}/{total} {elapsed}/{estimate}",
484             terminal_height => 0,
485             terminal_line => 0,
486             %args
487             }, ref $pkg || $pkg;
488             }
489              
490             sub progress_spinner_index {
491 356     356 1 670 my ($self, $val) = @_;
492 356 100       699 if (defined $val) {
493 23 50 33     212 if (ref $val || $val !~ m/\d+/) {
494 0         0 die 'progress_spinner_index should be a integer';
495             }
496 23         61 $self->{progress_spinner_index} = $val;
497             }
498 356         857 return $self->{progress_spinner_index};
499             }
500              
501             sub terminal_height {
502 46     46 1 111 my ($self, $val) = @_;
503 46 50       139 if (defined $val) {
504 0 0 0     0 if (ref $val || $val !~ m/\d+/) {
505 0         0 die 'terminal_height should be a integer';
506             }
507 0         0 $self->{terminal_height} = $val;
508             }
509 46         169 return $self->{terminal_height};
510             }
511              
512             sub terminal_line {
513 69     69 1 158 my ($self, $val) = @_;
514 69 100       161 if (defined $val) {
515 23 50 33     327 if (ref $val || $val !~ m/\d+/) {
516 0         0 die 'terminal_line should be a integer';
517             }
518 23         81 $self->{terminal_line} = $val;
519             }
520 69         198 return $self->{terminal_line};
521             }
522              
523             sub progress_spinners {
524 1190     1190 1 2997 my ($self, $val) = @_;
525 1190 50       3682 if (defined $val) {
526 0 0 0     0 if (ref $val || "" ne 'ARRAY') {
527 0         0 die 'progress_spinners should be a array';
528             }
529 0         0 $self->{progress_spinners} = $val;
530             }
531 1190         5205 return $self->{progress_spinners};
532             }
533              
534             sub savepos {
535 23     23 1 54 my $self = shift;
536 23 50       79 my ($col, $rows) = $self->terminal_height ? (0, $self->terminal_height) : (Term::Size::Any::chars($self->output));
537 23         53 my $x = '';
538 23 50       116 if ($self->terminal_line) {
    0          
539 23         63 $x = $self->terminal_line;
540             } elsif ($^O eq 'MSWin32') {
541 0         0 my $CONSOLE = Win32::Console->new(Win32::Console::STD_OUTPUT_HANDLE());
542 0         0 ($x) = $CONSOLE->Cursor();
543             } else {
544 0         0 system "stty cbreak /dev/tty 2>&1";
545 0         0 $self->output->print("\e[6n");
546 0         0 $x .= getc STDIN for 0 .. 5;
547 0         0 system "stty -cbreak /dev/tty 2>&1";
548 0         0 my($n, $m)=$x=~m/(\d+)\;(\d+)/;
549 0         0 $x = $n;
550 0         0 $self->clear();
551             }
552 23 50       78 if ($x == $rows) {
553 0         0 $x--;
554 0         0 for (@{ $self->progress_spinners }) {
  0         0  
555 0         0 $_->{savepos} = $_->{savepos} - 1;
556             }
557             }
558 23         70 $self->{savepos} = $x;
559             }
560              
561             sub loadpos {
562 724     724 1 2022 my $self = shift;
563 724         2952 my $pos = $self->{savepos};
564 724         2673 $self->output->print("\e[$pos;1f");
565             }
566              
567             sub start {
568 23     23 1 81 my ($self, $total) = @_;
569 23 50       139 $self->total($total) if $total;
570 23         178 $self->start_epoch(Time::HiRes::time);
571 23         70 $self->output->print("\e[?25l");
572 23         1669 $self->savepos;
573 23         67 $self->output->print("\n");
574 23         1538 my $ps = $self->new(%{$self});
  23         375  
575 23         79 push @{ $self->progress_spinners }, $ps;
  23         78  
576 23         73 $self->progress_spinner_index($self->progress_spinner_index + 1);
577 23         111 return $ps;
578             }
579            
580             sub advance {
581 1001     1001 1 4554 my ($self, $ps, $prevent) = @_;
582 1001 100       2805 if ($ps) {
583 633 100       1616 if ($ps->counter < $ps->total) {
584 520         1469 $ps->counter($ps->counter + 1);
585 520         1697 my $spinner = $ps->spinner;
586 520         2561 for (1 .. $spinner->{width}) {
587 1080         2827 my $index = $spinner->{index}->[$_ - 1];
588 1080         1845 $spinner->{index}->[$_ - 1] = ($index + 1) % scalar @{$spinner->{chars}};
  1080         3739  
589             }
590 520 50       1995 select(undef, undef, undef, $ps->slowed) if $ps->slowed;
591 520 100       17626 $ps->draw() unless $prevent;
592             } else {
593 113         356 $self->finish($ps);
594             }
595             } else {
596 368         704 for my $spinner (@{$self->progress_spinners}) {
  368         1082  
597 429         1906 $self->advance($spinner, 1);
598             }
599 368 100       1272 scalar @{$self->{progress_spinners}} ? $self->draw() : $self->finish;
  368         6675  
600             }
601             }
602              
603             sub time_advance_elapsed {
604 724     724 0 1822 my ($self) = @_;
605 724         1636 my %time = ();
606 724         2627 $time{epoch} = sprintf($self->precision, Time::HiRes::time);
607 724         2359 $time{start_epoch} = sprintf($self->precision, $self->start_epoch);
608 724   66     2923 $time{last_advance_epoch} = $self->last_advance_epoch || $time{start_epoch};
609 724         2118 $time{last_elapsed} = sprintf($self->precision, $time{epoch} - $time{last_advance_epoch}) + 0;
610 724         2129 $time{elapsed} = sprintf($self->precision, $time{epoch} - $time{start_epoch}) + 0;
611 724         2501 for (qw/epoch start_epoch last_advance_epoch last_elapsed elapsed/) {
612 3620         12220 $time{"${_}_second"} = int($time{$_});
613             }
614 724         2276 $self->last_advance_epoch($time{epoch});
615 724         8439 return %time;
616             }
617              
618             sub draw {
619 1186     1186 1 4162 my ($self, $ps) = @_;
620 1186 100       3430 if ($ps) {
621 724         3055 $ps->loadpos;
622 724         107134 $ps->clear();
623 724         3198 my ($spinner, $progress, $available, %options) = ($ps->spinner, $ps->progress, $ps->progress_width, $ps->time_advance_elapsed);
624 724         3680 $options{total} = $ps->total;
625 724         2297 $options{counter} = $ps->counter;
626 724         2387 $options{spinner} = color($ps->spinner_color);
627             $options{spinner} .= $spinner->{chars}->[
628             $spinner->{index}->[$_ - 1]
629 724         36892 ] for (1 .. $spinner->{width});
630 724         2936 $options{spinner} .= color($ps->text_color);
631 724         22499 $options{percent} = int( ( $options{counter} / $options{total} ) * 100 );
632 724         2951 $options{percentage} = ($available / 100) * $options{percent};
633 724 100       2518 $options{estimate} = $options{percent} ? sprintf($self->precision, (($options{elapsed} / $options{percent}) * 100) - $options{elapsed}) + 0 : 0;
634 724         2895 $options{estimate_second} = int($options{estimate} + 0.5);
635             $options{per_second} = $options{elapsed_seconds} ?
636 724 50       2573 int(($options{counter} / int($options{elapsed_second})) + 0.5)
637             : 0;
638             $options{progress} = sprintf("%s%s%s%s%s",
639             color($ps->progress_color),
640             $progress->{chars}->[0],
641             ( $progress->{chars}->[1] x int($options{percentage} + 0.5) ) . ( ' ' x int( ($available - $options{percentage}) + 0.5 ) ),
642 724         2761 $progress->{chars}->[2],
643             color($ps->text_color)
644             );
645 724         24846 $options{percentages} = $options{percentage} . '%';
646 724         2610 $options{percents} = $options{percent} . '%';
647             $options{$_} = sprintf ("%s%s%s",
648             color($ps->{$_ . "_color"}),
649             $options{$_},
650             color($ps->text_color)
651 724         3611 ) for (qw/total percent percents percentage counter per_second/);
652 724         20469 for (qw/elapsed last_elapsed estimate last_advance_epoch start_epoch epoch/) {
653             $options{$_} = sprintf ("%s%s%s",
654             color($ps->{$_ . "_color"}),
655 4344         99865 $options{$_},
656             color($ps->text_color)
657             );
658             $options{"${_}_second"} = sprintf ("%s%s%s",
659             color($ps->{$_ . "_color"}),
660 4344         120003 $options{"${_}_second"},
661             color($ps->text_color)
662             );
663             }
664 724         19713 my $message = $ps->message;
665 724         24505 $message =~ s/$VALIDATE{msg_regex}/$options{$1}/ig;
666 724         2840 $message .= color('reset') . "\n";
667 724         19009 $ps->output->print($message);
668 724         74698 return $ps->drawn(1);
669             } else {
670 462         1266 for my $spinner (@{$self->progress_spinners}) {
  462         2617  
671 724         2629 $self->draw($spinner);
672             }
673 462         1573 return $self->drawn(1);
674             }
675             }
676            
677             sub finish {
678 129     129 1 312 my ($self, $sp) = @_;
679 129 100 100     428 if ($sp && scalar @{$self->progress_spinners}) {
  113         216  
680 112         200 my $i = 0;
681 112         174 for (@{ $self->progress_spinners }) {
  112         223  
682 155 100       392 if ($sp->progress_spinner_index == $_->progress_spinner_index) {
683 23         61 last;
684             }
685 132         264 $i++;
686             }
687 112         185 splice @{$self->progress_spinners}, $i, 1;
  112         265  
688              
689             } else {
690 17         47 $self->output->print("\e[?25h");
691 17         1305 $self->finished(1);
692             }
693 129         432 return 0;
694             }
695              
696             sub finished {
697 273 100   273 0 717 if (defined $_[1]) {
698 17         59 $_[0]->{finished} = $_[1];
699             }
700 273         620 $_[0]->{finished};
701             }
702              
703             sub drawn {
704 1925     1925 1 5643 my ($self, $val) = @_;
705 1925 50       6040 if (defined $val) {
706 1925         4185 $self->{drawn} = $val;
707             }
708 1925         11419 return $self->{drawn};
709             }
710            
711             sub clear {
712 739     739 1 7719 my ($self) = @_;
713 739         2192 $self->output->print("\r\e[2K");
714 739         50150 $self->drawn(0);
715             }
716              
717             sub message {
718 724     724 1 1928 my ($self, $val) = @_;
719 724 50       1992 if (defined $val) {
720 0 0       0 if (ref $val) {
721 0         0 die 'message should be a string';
722             }
723 0         0 $self->{message} = $val;
724             }
725 724         2328 return $self->{message};
726             }
727              
728             sub output {
729 2253     2253 1 5327 my ($self, $val) = @_;
730 2253 50       6349 if (defined $val) {
731 0         0 $self->{output} = $val;
732             }
733 2253         15155 return $self->{output};
734             }
735              
736             sub total {
737 1380     1380 1 3074 my ($self, $val) = @_;
738 1380 100       3534 if (defined $val) {
739 23 50       118 if ($val !~ m/\d+/) {
740 0         0 die "total should be a integer";
741             }
742 23         65 $self->{total} = $val;
743 23         51 $self->{counter} = 0;
744             }
745 1380         4648 return $self->{total};
746             }
747              
748             sub slowed {
749 1042     1042 1 2361 my ($self, $val) = @_;
750 1042 100       2757 if (defined $val) {
751 2 50       55 if ($val !~ m/\d+(\.\d+)?/) {
752 0         0 die "slowed should be a float";
753             }
754 2         41 $self->{slowed} = $val;
755             }
756 1042         52117165 return $self->{slowed};
757             }
758              
759             sub counter {
760 2397     2397 1 4638 my ($self, $val) = @_;
761 2397 100       5621 if (defined $val) {
762 520 50       4154 if ($val !~ m/\d+/) {
763 0         0 die "counter should be a integer";
764             }
765 520         1410 $self->{counter} = $val;
766             }
767 2397         7373 return $self->{counter};
768             }
769              
770             sub start_epoch {
771 747     747 1 2228 my ($self, $val) = @_;
772 747 100       2341 if (defined $val) {
773 23 50       371 if ($val !~ m/\d+(\.\d+)?/) {
774 0         0 die "start_epoch should be a epoch";
775             }
776 23         68 $self->{start_epoch} = $val;
777             }
778 747         5418 return $self->{start_epoch};
779             }
780              
781             sub last_advance_epoch {
782 1448     1448 1 3269 my ($self, $val) = @_;
783 1448 100       3539 if (defined $val) {
784 724 50       9594 if ($val !~ m/\d+(\.\d+)?/) {
785 0         0 die "last_advance_epoch should be a epoch";
786             }
787 724         1906 $self->{last_advance_epoch} = $val;
788             }
789 1448         4552 return $self->{last_advance_epoch};
790             }
791              
792             sub precision {
793 3614     3614 1 7414 my ($self, $val) = @_;
794 3614 50       7526 if (defined $val) {
795 0 0       0 if ($val !~ m/\d+/) {
796 0         0 die "last_advance_epoch should be a epoch";
797             }
798 0         0 $self->{precision} = $val;
799             }
800 3614         7662 $val = $self->{precision};
801 3614         43491 return "%.${val}f";
802             }
803              
804             sub text_color {
805 14480     14480 1 408720 my ($self, $val) = @_;
806 14480 50       28459 if (defined $val) {
807 0 0       0 unless ($VALIDATE{colours}{$val}) {
808 0         0 die "$val is not a valid color";
809             }
810 0         0 $self->{text_color} = $val;
811             }
812 14480         34992 return $self->{text_color};
813             }
814              
815             sub spinner {
816 1259     1259 1 3601 my ($self, $spinner) = @_;
817 1259 100 50     3920 $self->{spinner_options} = $SPINNERS{$spinner} or die "Invalid spinner $spinner" if $spinner;
818 1259         4605 $self->{spinner_options};
819             }
820              
821             sub spinner_color {
822 724     724 1 1499 my ($self, $val) = @_;
823 724 50       2090 if (defined $val) {
824 0 0       0 unless ($VALIDATE{colours}{$val}) {
825 0         0 die "$val is not a valid color";
826             }
827 0         0 $self->{spinner_color} = $val;
828             }
829 724         5343 return $self->{spinner_color};
830             }
831              
832             sub progress {
833 739     739 1 1969 my ($self, $progress) = @_;
834 739 100 50     2157 $self->{progress_options} = $PROGRESS{$progress} or die "Invalid progress $progress" if $progress;
835 739         2949 $self->{progress_options};
836             }
837              
838             sub progress_color {
839 724     724 1 1721 my ($self, $val) = @_;
840 724 50       1972 if (defined $val) {
841 0 0       0 unless ($VALIDATE{colours}{$val}) {
842 0         0 die "$val is not a valid color";
843             }
844 0         0 $self->{progress_color} = $val;
845             }
846 724         2586 return $self->{progress_color};
847             }
848              
849             sub progress_width {
850 724     724 1 2002 my ($self, $val) = @_;
851 724 50       2216 if (defined $val) {
852 0         0 $self->{progress_width} = $val;
853             }
854 724         2598 return $self->{progress_width};
855             }
856              
857             sub percent_color {
858 0     0 1 0 my ($self, $val) = @_;
859 0 0       0 if (defined $val) {
860 0 0       0 unless ($VALIDATE{colours}{$val}) {
861 0         0 die "$val is not a valid color";
862             }
863 0         0 $self->{percent_color} = $val;
864             }
865 0         0 return $self->{percent_color};
866             }
867              
868             sub percents_color {
869 0     0 1 0 my ($self, $val) = @_;
870 0 0       0 if (defined $val) {
871 0 0       0 unless ($VALIDATE{colours}{$val}) {
872 0         0 die "$val is not a valid color";
873             }
874 0         0 $self->{percents_color} = $val;
875             }
876 0         0 return $self->{percents_color};
877             }
878              
879             sub percentage_color {
880 0     0 1 0 my ($self, $val) = @_;
881 0 0       0 if (defined $val) {
882 0 0       0 unless ($VALIDATE{colours}{$val}) {
883 0         0 die "$val is not a valid color";
884             }
885 0         0 $self->{percentage_color} = $val;
886             }
887 0         0 return $self->{percentage_color};
888             }
889              
890             sub percentages_color {
891 0     0 1 0 my ($self, $val) = @_;
892 0 0       0 if (defined $val) {
893 0 0       0 unless ($VALIDATE{colours}{$val}) {
894 0         0 die "$val is not a valid color";
895             }
896 0         0 $self->{percentages_color} = $val;
897             }
898 0         0 return $self->{percentages_color};
899             }
900              
901             sub total_color {
902 0     0 1 0 my ($self, $val) = @_;
903 0 0       0 if (defined $val) {
904 0 0       0 unless ($VALIDATE{colours}{$val}) {
905 0         0 die "$val is not a valid color";
906             }
907 0         0 $self->{total_color} = $val;
908             }
909 0         0 return $self->{total_color};
910             }
911              
912             sub counter_color {
913 0     0 1 0 my ($self, $val) = @_;
914 0 0       0 if (defined $val) {
915 0 0       0 unless ($VALIDATE{colours}{$val}) {
916 0         0 die "$val is not a valid color";
917             }
918 0         0 $self->{counter_color} = $val;
919             }
920 0         0 return $self->{counter_color};
921             }
922              
923             sub elapsed_color {
924 0     0 1 0 my ($self, $val) = @_;
925 0 0       0 if (defined $val) {
926 0 0       0 unless ($VALIDATE{colours}{$val}) {
927 0         0 die "$val is not a valid color";
928             }
929 0         0 $self->{elapsed_color} = $val;
930             }
931 0         0 return $self->{elapsed_color};
932             }
933              
934             sub last_elapsed_color {
935 0     0 1 0 my ($self, $val) = @_;
936 0 0       0 if (defined $val) {
937 0 0       0 unless ($VALIDATE{colours}{$val}) {
938 0         0 die "$val is not a valid color";
939             }
940 0         0 $self->{last_elapsed_color} = $val;
941             }
942 0         0 return $self->{last_elapsed_color};
943             }
944              
945             sub estimate_color {
946 0     0 1 0 my ($self, $val) = @_;
947 0 0       0 if (defined $val) {
948 0 0       0 unless ($VALIDATE{colours}{$val}) {
949 0         0 die "$val is not a valid color";
950             }
951 0         0 $self->{estimate_elapsed_color} = $val;
952             }
953 0         0 return $self->{estimate_elapsed_color};
954             }
955              
956             sub last_advance_epoch_color {
957 0     0 1 0 my ($self, $val) = @_;
958 0 0       0 if (defined $val) {
959 0 0       0 unless ($VALIDATE{colours}{$val}) {
960 0         0 die "$val is not a valid color";
961             }
962 0         0 $self->{last_advance_epoch_color} = $val;
963             }
964 0         0 return $self->{last_advance_epoch_color};
965             }
966              
967             sub start_epoch_color {
968 0     0 1 0 my ($self, $val) = @_;
969 0 0       0 if (defined $val) {
970 0 0       0 unless ($VALIDATE{colours}{$val}) {
971 0         0 die "$val is not a valid color";
972             }
973 0         0 $self->{start_epoch_color} = $val;
974             }
975 0         0 return $self->{start_epoch_color};
976             }
977              
978             sub epoch_color {
979 0     0 1 0 my ($self, $val) = @_;
980 0 0       0 if (defined $val) {
981 0 0       0 unless ($VALIDATE{colours}{$val}) {
982 0         0 die "$val is not a valid color";
983             }
984 0         0 $self->{epoch_color} = $val;
985             }
986 0         0 return $self->{epoch_color};
987             }
988              
989             sub per_second_color {
990 0     0 1 0 my ($self, $val) = @_;
991 0 0       0 if (defined $val) {
992 0 0       0 unless ($VALIDATE{colours}{$val}) {
993 0         0 die "$val is not a valid color";
994             }
995 0         0 $self->{per_second_color} = $val;
996             }
997 0         0 return $self->{per_second_color};
998             }
999              
1000             sub sleep {
1001 16     16 1 1754022 select(undef, undef, undef, $_[1]);
1002             }
1003              
1004             1;
1005              
1006             __END__;