File Coverage

blib/lib/Log/Progress/Parser.pm
Criterion Covered Total %
statement 57 61 93.4
branch 25 34 73.5
condition 9 12 75.0
subroutine 5 5 100.0
pod 2 2 100.0
total 98 114 85.9


line stmt bran cond sub pod time code
1             package Log::Progress::Parser;
2             $Log::Progress::Parser::VERSION = '0.12';
3 5     5   6363 use Moo 2;
  5         55737  
  5         33  
4 5     5   10174 use JSON;
  5         40629  
  5         33  
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 2413085 my $self= shift;
19 28         150 my $fh= $self->input;
20 28 100       189 if (!ref $fh) {
21 19         38 my $input= $fh;
22 19         31 undef $fh;
23 2 50   2   14 open $fh, '<', \$input or die "open(scalar): $!";
  2         4  
  2         15  
  19         335  
24             }
25 28 100       1907 if ($self->input_pos) {
26 18 50       241 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         99 my $pos;
32             my %parent_cleanup;
33 28         83 local $_;
34 28         378 while (<$fh>) {
35 170 100       496 last unless substr($_,-1) eq "\n";
36 169         330 $pos= tell($fh);
37 169 100       918 next unless $_ =~ /^progress: (([[:alpha:]][\w.]*) )?(.*)/;
38 166         558 my ($step_id, $remainder)= ($2, $3);
39 166         440 my $state= $self->step_state($step_id, 1, \my @state_parent);
40             # First, check for progress number followed by optional message
41 166 100       713 if ($remainder =~ m,^([\d.]+)(/(\d+))?( (.*))?,) {
    50          
    0          
42 151         496 my ($num, $denom, $message)= ($1, $3, $5);
43 151 100       281 if (defined $message) {
44 20         105 $message =~ s/^- //; # "- " is optional syntax
45 20         60 $state->{message}= $message;
46             } else {
47 131 100 100     626 $state->{message}= '' if !defined $state->{message} or !$self->sticky_message;
48             }
49 151         409 $state->{progress}= $num+0;
50 151 100       314 if (defined $denom) {
51 120         193 $state->{current}= $num;
52 120         197 $state->{total}= $denom;
53 120         218 $state->{progress} /= $denom;
54             }
55 151 100       337 if ($state->{contribution}) {
56             # Need to apply progress to parent nodes at end
57             $parent_cleanup{$state_parent[$_]}= [ $_, $state_parent[$_] ]
58 139         1011 for 0..$#state_parent;
59             }
60             }
61             elsif ($remainder =~ m,^\(([\d.]+)\) (.*),) {
62 15         68 my ($contribution, $title)= ($1, $2);
63 15         32 $title =~ s/^- //; # "- " is optional syntax
64 15         39 $state->{title}= $title;
65 15         106 $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         157 $self->input_pos($pos);
78             # apply child progress contributions to parent nodes
79 28         134 for (sort { $b->[0] <=> $a->[0] } values %parent_cleanup) {
  0         0  
80 13         36 my $state= $_->[1];
81 13         32 $state->{progress}= 0;
82 13         23 for (values %{$state->{step}}) {
  13         62  
83             $state->{progress} += $_->{progress} * $_->{contribution}
84 31 50 66     272 if $_->{progress} && $_->{contribution};
85             }
86             }
87 28         281 return $self->state;
88             }
89              
90              
91             sub step_state {
92 166     166 1 325 my ($self, $step_id, $create, $path)= @_;
93 166         315 my $state= $self->state;
94 166 100 66     638 if (defined $step_id and length $step_id) {
95 154         415 for (split /\./, $step_id) {
96 154 50       398 push @$path, $state if $path;
97 154   66     468 $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         389 $state;
105             }
106              
107             1;
108              
109             __END__