File Coverage

blib/lib/Log/Progress/Parser.pm
Criterion Covered Total %
statement 57 61 93.4
branch 25 34 73.5
condition 8 12 66.6
subroutine 5 5 100.0
pod 2 2 100.0
total 97 114 85.0


line stmt bran cond sub pod time code
1             package Log::Progress::Parser;
2             $Log::Progress::Parser::VERSION = '0.11';
3 5     5   4238 use Moo 2;
  5         45637  
  5         27  
4 5     5   5145 use JSON;
  5         8  
  5         31  
5              
6             # ABSTRACT: Parse progress data from a file
7              
8              
9             has input => ( is => 'rw' );
10             has input_pos => ( is => 'rw' );
11             has state => ( is => 'rw', default => sub { {} } );
12             *status= *state; # alias, since I changed the API
13             has sticky_message => ( is => 'rw' );
14             has on_data => ( is => 'rw' );
15              
16              
17             sub parse {
18 28     28 1 2406951 my $self= shift;
19 28         98 my $fh= $self->input;
20 28 100       108 if (!ref $fh) {
21 19         20 my $input= $fh;
22 19         15 undef $fh;
23 2 50   2   9 open $fh, '<', \$input or die "open(scalar): $!";
  2         2  
  2         11  
  19         185  
24             }
25 28 100       1324 if ($self->input_pos) {
26 18 50       113 seek($fh, $self->input_pos, 0)
27             or die "seek: $!";
28             }
29             # TODO: If input is seekable, then seek to end and work backward
30             # Substeps will make that rather complicated.
31 28         33 my $pos;
32             my %parent_cleanup;
33 28         35 local $_;
34 28         215 while (<$fh>) {
35 170 100       337 last unless substr($_,-1) eq "\n";
36 169         210 $pos= tell($fh);
37 169 100       678 next unless $_ =~ /^progress: (([[:alpha:]][\w.]*) )?(.*)/;
38 166         331 my ($step_id, $remainder)= ($2, $3);
39 166         298 my $state= $self->step_state($step_id, 1, \my @state_parent);
40             # First, check for progress number followed by optional message
41 166 100       527 if ($remainder =~ m,^([\d.]+)(/(\d+))?( (.*))?,) {
    50          
    0          
42 151         272 my ($num, $denom, $message)= ($1, $3, $5);
43 151 100       173 if (defined $message) {
44 20         57 $message =~ s/^- //; # "- " is optional syntax
45 20         42 $state->{message}= $message;
46             } else {
47 131 100 66     395 $state->{message}= '' if !defined $state->{message} or !$self->sticky_message;
48             }
49 151         233 $state->{progress}= $num+0;
50 151 100       223 if (defined $denom) {
51 120         108 $state->{current}= $num;
52 120         115 $state->{total}= $denom;
53 120         139 $state->{progress} /= $denom;
54             }
55 151 100       231 if ($state->{contribution}) {
56             # Need to apply progress to parent nodes at end
57             $parent_cleanup{$state_parent[$_]}= [ $_, $state_parent[$_] ]
58 139         840 for 0..$#state_parent;
59             }
60             }
61             elsif ($remainder =~ m,^\(([\d.]+)\) (.*),) {
62 15         26 my ($contribution, $title)= ($1, $2);
63 15         15 $title =~ s/^- //; # "- " is optional syntax
64 15         21 $state->{title}= $title;
65 15         60 $state->{contribution}= $contribution+0;
66             }
67             elsif ($remainder =~ /^\{/) {
68 0         0 my $data= JSON->new->decode($remainder);
69 0 0       0 $state->{data}= !defined $self->on_data? $data
70             : $self->on_data->($self, $step_id, $data);
71             }
72             else {
73 0         0 warn "can't parse progress message \"$remainder\"\n";
74             }
75             }
76             # Mark file position for next call
77 28         61 $self->input_pos($pos);
78             # apply child progress contributions to parent nodes
79 28         78 for (sort { $b->[0] <=> $a->[0] } values %parent_cleanup) {
  0         0  
80 13         22 my $state= $_->[1];
81 13         19 $state->{progress}= 0;
82 13         17 for (values %{$state->{step}}) {
  13         40  
83             $state->{progress} += $_->{progress} * $_->{contribution}
84 31 50 66     134 if $_->{progress} && $_->{contribution};
85             }
86             }
87 28         156 return $self->state;
88             }
89              
90              
91             sub step_state {
92 166     166 1 157 my ($self, $step_id, $create, $path)= @_;
93 166         206 my $state= $self->state;
94 166 100 66     536 if (defined $step_id and length $step_id) {
95 154         274 for (split /\./, $step_id) {
96 154 50       248 push @$path, $state if $path;
97 154   66     337 $state= ($state->{step}{$_} or do {
98             return undef unless $create;
99             my $idx= scalar(keys %{$state->{step}});
100             $state->{step}{$_}= { idx => $idx };
101             });
102             }
103             }
104 166         244 $state;
105             }
106              
107             1;
108              
109             __END__