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 = '0.05';
3 3     3   214029 use 5.006; use strict; use warnings;
  3     3   31  
  3     3   17  
  3         7  
  3         92  
  3         26  
  3         6  
  3         86  
4 3     3   1750 use IO::Handle; use Term::ANSIColor; use Time::HiRes qw//;
  3     3   19648  
  3     3   145  
  3         2016  
  3         27596  
  3         418  
  3         2046  
  3         4638  
  3         100  
5 3     3   1732 use Term::Size::Any qw/chars/;
  3         864  
  3         20  
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   9604 my $c = $_;
  24         50  
14             (
15             $c => 1,
16             "bright_${c}" => 1,
17 24         43 (map { (
18 192         1347 "${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         199 %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         16 $SPINNERS{default} = $SPINNERS{bar};
402 3         132 %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         13313 $PROGRESS{default} = $PROGRESS{bar};
453             }
454              
455             sub new {
456 25 50   25 1 737 my ($pkg, %args) = (shift, ref $_[0] ? %{$_[0]} : @_);
  0         0  
457             $args{$_} and ($VALIDATE{colours}{$args{$_}} or die "Invalid color for $_")
458 25   50     768 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     123 $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     1306 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 645 my ($self, $val) = @_;
492 356 100       789 if (defined $val) {
493 23 50 33     227 if (ref $val || $val !~ m/\d+/) {
494 0         0 die 'progress_spinner_index should be a integer';
495             }
496 23         67 $self->{progress_spinner_index} = $val;
497             }
498 356         936 return $self->{progress_spinner_index};
499             }
500              
501             sub terminal_height {
502 46     46 1 116 my ($self, $val) = @_;
503 46 50       117 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         194 return $self->{terminal_height};
510             }
511              
512             sub terminal_line {
513 69     69 1 165 my ($self, $val) = @_;
514 69 100       165 if (defined $val) {
515 23 50 33     386 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         233 return $self->{terminal_line};
521             }
522              
523             sub progress_spinners {
524 1190     1190 1 3159 my ($self, $val) = @_;
525 1190 50       3949 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         5491 return $self->{progress_spinners};
532             }
533              
534             sub savepos {
535 23     23 1 70 my $self = shift;
536 23 50       85 my ($col, $rows) = $self->terminal_height ? (0, $self->terminal_height) : (Term::Size::Any::chars($self->output));
537 23         56 my $x = '';
538 23 50       68 if ($self->terminal_line) {
    0          
539 23         62 $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       77 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         77 $self->{savepos} = $x;
559             }
560              
561             sub loadpos {
562 724     724 1 2425 my $self = shift;
563 724         3128 my $pos = $self->{savepos};
564 724         3171 $self->output->print("\e[$pos;1f");
565             }
566              
567             sub start {
568 23     23 1 79 my ($self, $total) = @_;
569 23 50       117 $self->total($total) if $total;
570 23         167 $self->start_epoch(Time::HiRes::time);
571 23         68 $self->output->print("\e[?25l");
572 23         1518 $self->savepos;
573 23         65 $self->output->print("\n");
574 23         1518 my $ps = $self->new(%{$self});
  23         392  
575 23         88 push @{ $self->progress_spinners }, $ps;
  23         91  
576 23         98 $self->progress_spinner_index($self->progress_spinner_index + 1);
577 23         122 return $ps;
578             }
579            
580             sub advance {
581 1001     1001 1 4937 my ($self, $ps, $prevent) = @_;
582 1001 100       2610 if ($ps) {
583 633 100       1717 if ($ps->counter < $ps->total) {
584 520         1469 $ps->counter($ps->counter + 1);
585 520         1767 my $spinner = $ps->spinner;
586 520         2362 for (1 .. $spinner->{width}) {
587 1080         2747 my $index = $spinner->{index}->[$_ - 1];
588 1080         2178 $spinner->{index}->[$_ - 1] = ($index + 1) % scalar @{$spinner->{chars}};
  1080         3638  
589             }
590 520 50       1770 select(undef, undef, undef, $ps->slowed) if $ps->slowed;
591 520 100       20070 $ps->draw() unless $prevent;
592             } else {
593 113         382 $self->finish($ps);
594             }
595             } else {
596 368         860 for my $spinner (@{$self->progress_spinners}) {
  368         903  
597 429         1981 $self->advance($spinner, 1);
598             }
599 368 100       1849 scalar @{$self->{progress_spinners}} ? $self->draw() : $self->finish;
  368         6611  
600             }
601             }
602              
603             sub time_advance_elapsed {
604 724     724 0 1763 my ($self) = @_;
605 724         1806 my %time = ();
606 724         2587 $time{epoch} = sprintf($self->precision, Time::HiRes::time);
607 724         2564 $time{start_epoch} = sprintf($self->precision, $self->start_epoch);
608 724   66     2955 $time{last_advance_epoch} = $self->last_advance_epoch || $time{start_epoch};
609 724         2156 $time{last_elapsed} = sprintf($self->precision, $time{epoch} - $time{last_advance_epoch}) + 0;
610 724         2242 $time{elapsed} = sprintf($self->precision, $time{epoch} - $time{start_epoch}) + 0;
611 724         2726 for (qw/epoch start_epoch last_advance_epoch last_elapsed elapsed/) {
612 3620         13002 $time{"${_}_second"} = int($time{$_});
613             }
614 724         2737 $self->last_advance_epoch($time{epoch});
615 724         8907 return %time;
616             }
617              
618             sub draw {
619 1186     1186 1 4602 my ($self, $ps) = @_;
620 1186 100       3453 if ($ps) {
621 724         3592 $ps->loadpos;
622 724         94384 $ps->clear();
623 724         3382 my ($spinner, $progress, $available, %options) = ($ps->spinner, $ps->progress, $ps->progress_width, $ps->time_advance_elapsed);
624 724         3316 $options{total} = $ps->total;
625 724         2418 $options{counter} = $ps->counter;
626 724         2499 $options{spinner} = color($ps->spinner_color);
627             $options{spinner} .= $spinner->{chars}->[
628             $spinner->{index}->[$_ - 1]
629 724         38415 ] for (1 .. $spinner->{width});
630 724         2788 $options{spinner} .= color($ps->text_color);
631 724         22948 $options{percent} = int( ( $options{counter} / $options{total} ) * 100 );
632 724         3082 $options{percentage} = ($available / 100) * $options{percent};
633 724 100       2885 $options{estimate} = $options{percent} ? sprintf($self->precision, (($options{elapsed} / $options{percent}) * 100) - $options{elapsed}) + 0 : 0;
634 724         2792 $options{estimate_second} = int($options{estimate} + 0.5);
635             $options{per_second} = $options{elapsed_seconds} ?
636 724 50       2712 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         2444 $progress->{chars}->[2],
643             color($ps->text_color)
644             );
645 724         25271 $options{percentages} = $options{percentage} . '%';
646 724         2271 $options{percents} = $options{percent} . '%';
647             $options{$_} = sprintf ("%s%s%s",
648             color($ps->{$_ . "_color"}),
649             $options{$_},
650             color($ps->text_color)
651 724         3668 ) for (qw/total percent percents percentage counter per_second/);
652 724         20075 for (qw/elapsed last_elapsed estimate last_advance_epoch start_epoch epoch/) {
653             $options{$_} = sprintf ("%s%s%s",
654             color($ps->{$_ . "_color"}),
655 4344         101900 $options{$_},
656             color($ps->text_color)
657             );
658             $options{"${_}_second"} = sprintf ("%s%s%s",
659             color($ps->{$_ . "_color"}),
660 4344         121477 $options{"${_}_second"},
661             color($ps->text_color)
662             );
663             }
664 724         20232 my $message = $ps->message;
665 724         26127 $message =~ s/$VALIDATE{msg_regex}/$options{$1}/ig;
666 724         3360 $message .= color('reset') . "\n";
667 724         20429 $ps->output->print($message);
668 724         105509 return $ps->drawn(1);
669             } else {
670 462         1454 for my $spinner (@{$self->progress_spinners}) {
  462         2750  
671 724         2744 $self->draw($spinner);
672             }
673 462         1398 return $self->drawn(1);
674             }
675             }
676            
677             sub finish {
678 129     129 1 272 my ($self, $sp) = @_;
679 129 100 100     399 if ($sp && scalar @{$self->progress_spinners}) {
  113         278  
680 112         259 my $i = 0;
681 112         204 for (@{ $self->progress_spinners }) {
  112         221  
682 155 100       403 if ($sp->progress_spinner_index == $_->progress_spinner_index) {
683 23         57 last;
684             }
685 132         284 $i++;
686             }
687 112         226 splice @{$self->progress_spinners}, $i, 1;
  112         223  
688              
689             } else {
690 17         42 $self->output->print("\e[?25h");
691 17         1216 $self->finished(1);
692             }
693 129         451 return 0;
694             }
695              
696             sub finished {
697 273 100   273 0 861 if (defined $_[1]) {
698 17         54 $_[0]->{finished} = $_[1];
699             }
700 273         671 $_[0]->{finished};
701             }
702              
703             sub drawn {
704 1925     1925 1 4918 my ($self, $val) = @_;
705 1925 50       5520 if (defined $val) {
706 1925         3926 $self->{drawn} = $val;
707             }
708 1925         10637 return $self->{drawn};
709             }
710            
711             sub clear {
712 739     739 1 7887 my ($self) = @_;
713 739         2313 $self->output->print("\r\e[2K");
714 739         46993 $self->drawn(0);
715             }
716              
717             sub message {
718 724     724 1 1838 my ($self, $val) = @_;
719 724 50       1878 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         1867 return $self->{message};
726             }
727              
728             sub output {
729 2253     2253 1 5780 my ($self, $val) = @_;
730 2253 50       6804 if (defined $val) {
731 0         0 $self->{output} = $val;
732             }
733 2253         16435 return $self->{output};
734             }
735              
736             sub total {
737 1380     1380 1 3404 my ($self, $val) = @_;
738 1380 100       5110 if (defined $val) {
739 23 50       124 if ($val !~ m/\d+/) {
740 0         0 die "total should be a integer";
741             }
742 23         60 $self->{total} = $val;
743 23         54 $self->{counter} = 0;
744             }
745 1380         4997 return $self->{total};
746             }
747              
748             sub slowed {
749 1042     1042 1 2382 my ($self, $val) = @_;
750 1042 100       2488 if (defined $val) {
751 2 50       48 if ($val !~ m/\d+(\.\d+)?/) {
752 0         0 die "slowed should be a float";
753             }
754 2         27 $self->{slowed} = $val;
755             }
756 1042         52117576 return $self->{slowed};
757             }
758              
759             sub counter {
760 2397     2397 1 5328 my ($self, $val) = @_;
761 2397 100       5538 if (defined $val) {
762 520 50       5361 if ($val !~ m/\d+/) {
763 0         0 die "counter should be a integer";
764             }
765 520         1394 $self->{counter} = $val;
766             }
767 2397         7273 return $self->{counter};
768             }
769              
770             sub start_epoch {
771 747     747 1 2171 my ($self, $val) = @_;
772 747 100       2072 if (defined $val) {
773 23 50       400 if ($val !~ m/\d+(\.\d+)?/) {
774 0         0 die "start_epoch should be a epoch";
775             }
776 23         61 $self->{start_epoch} = $val;
777             }
778 747         5203 return $self->{start_epoch};
779             }
780              
781             sub last_advance_epoch {
782 1448     1448 1 3915 my ($self, $val) = @_;
783 1448 100       3696 if (defined $val) {
784 724 50       10773 if ($val !~ m/\d+(\.\d+)?/) {
785 0         0 die "last_advance_epoch should be a epoch";
786             }
787 724         1802 $self->{last_advance_epoch} = $val;
788             }
789 1448         4988 return $self->{last_advance_epoch};
790             }
791              
792             sub precision {
793 3614     3614 1 6831 my ($self, $val) = @_;
794 3614 50       7490 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         6778 $val = $self->{precision};
801 3614         47049 return "%.${val}f";
802             }
803              
804             sub text_color {
805 14480     14480 1 416672 my ($self, $val) = @_;
806 14480 50       28962 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         34804 return $self->{text_color};
813             }
814              
815             sub spinner {
816 1259     1259 1 3055 my ($self, $spinner) = @_;
817 1259 100 50     3609 $self->{spinner_options} = $SPINNERS{$spinner} or die "Invalid spinner $spinner" if $spinner;
818 1259         4380 $self->{spinner_options};
819             }
820              
821             sub spinner_color {
822 724     724 1 1827 my ($self, $val) = @_;
823 724 50       2120 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         5305 return $self->{spinner_color};
830             }
831              
832             sub progress {
833 739     739 1 2181 my ($self, $progress) = @_;
834 739 100 50     2878 $self->{progress_options} = $PROGRESS{$progress} or die "Invalid progress $progress" if $progress;
835 739         3276 $self->{progress_options};
836             }
837              
838             sub progress_color {
839 724     724 1 1778 my ($self, $val) = @_;
840 724 50       2193 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         2725 return $self->{progress_color};
847             }
848              
849             sub progress_width {
850 724     724 1 2436 my ($self, $val) = @_;
851 724 50       2136 if (defined $val) {
852 0         0 $self->{progress_width} = $val;
853             }
854 724         3163 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 1753924 select(undef, undef, undef, $_[1]);
1002             }
1003              
1004             1;
1005              
1006             __END__;