File Coverage

blib/lib/Directory/Scanner/StreamBuilder/Recursive.pm
Criterion Covered Total %
statement 65 68 95.5
branch 10 14 71.4
condition 2 5 40.0
subroutine 15 16 93.7
pod 1 7 14.2
total 93 110 84.5


line stmt bran cond sub pod time code
1             package Directory::Scanner::StreamBuilder::Recursive;
2             # ABSTRACT: Recrusive streaming directory iterator
3              
4 8     8   45 use strict;
  8         41  
  8         198  
5 8     8   35 use warnings;
  8         16  
  8         160  
6              
7 8     8   65 use Carp ();
  8         13  
  8         104  
8 8     8   35 use Scalar::Util ();
  8         16  
  8         121  
9              
10 8     8   43 use UNIVERSAL::Object;
  8         13  
  8         171  
11 8     8   39 use Directory::Scanner::API::Stream;
  8         18  
  8         415  
12              
13             our $VERSION = '0.01';
14             our $AUTHORITY = 'cpan:STEVAN';
15              
16 8   50 8   43 use constant DEBUG => $ENV{DIR_SCANNER_STREAM_RECURSIVE_DEBUG} // 0;
  8         20  
  8         659  
17              
18             ## ...
19              
20 8     8   751 our @ISA; BEGIN { @ISA = ('UNIVERSAL::Object', 'Directory::Scanner::API::Stream') }
21             our %HAS; BEGIN {
22             %HAS = (
23             stream => sub {},
24             # internal state ...
25             _head => sub {},
26 12         115 _stack => sub { [] },
27 12         88 _is_done => sub { 0 },
28 12         165 _is_closed => sub { 0 },
29             )
30 8     8   3205 }
31              
32             ## ...
33              
34             sub BUILD {
35 12     12 1 410 my ($self, $params) = @_;
36              
37 12         37 my $stream = $self->{stream};
38              
39 12 50 33     147 (Scalar::Util::blessed($stream) && $stream->DOES('Directory::Scanner::API::Stream'))
40             || Carp::confess 'You must supply a directory stream';
41              
42 12         39 push @{$self->{_stack}} => $stream;
  12         49  
43             }
44              
45             sub clone {
46 0     0 0 0 my ($self, $dir) = @_;
47 0         0 return $self->new( stream => $self->{stream}->clone( $dir ) );
48             }
49              
50             ## accessor
51              
52 44     44 0 2768 sub head { $_[0]->{_head} }
53              
54 39     39 0 11239 sub is_done { $_[0]->{_is_done} }
55 33     33 0 2307 sub is_closed { $_[0]->{_is_closed} }
56              
57             sub close {
58 12     12 0 430 my $self = $_[0];
59 12         27 while ( my $stream = pop @{ $self->{_stack} } ) {
  12         62  
60 0         0 $stream->close;
61             }
62 12         32 $self->{_is_closed} = 1;
63 12         34 return;
64             }
65              
66             sub next {
67 95     95 0 7361 my $self = $_[0];
68              
69 95 50       240 return if $self->{_is_done};
70              
71             Carp::confess 'Cannot call `next` on a closed stream'
72 95 50       213 if $self->{_is_closed};
73              
74 95         147 my $next;
75 95         137 while (1) {
76 148         631 undef $next; # clear any previous values, just cause ...
77 148         219 $self->_log('Entering loop ... ') if DEBUG;
78              
79 148 100       362 if ( my $current = $self->{_stack}->[-1] ) {
80 136         202 $self->_log('Stream available in stack') if DEBUG;
81 136 100       353 if ( my $candidate = $current->next ) {
82             # if we have a directory, prepare
83             # to recurse into it the next time
84             # we are called, then ....
85 83 100       228 if ( $candidate->is_dir ) {
86 41         411 push @{$self->{_stack}} => $current->clone( $candidate );
  41         142  
87             }
88            
89             # return our successful candidate
90 83         663 $next = $candidate;
91 83         165 last;
92             }
93             else {
94 53         76 $self->_log('Current stream has been exhausted, moving to next') if DEBUG;
95              
96             # something, something, ... check is_done on $current here ...
97              
98 53         80 my $old = pop @{$self->{_stack}};
  53         124  
99 53 50       147 $old->close unless $old->is_closed;
100 53         201 next;
101             }
102             }
103             else {
104 12         24 $self->_log('No more streams available in stack') if DEBUG;
105 12         22 $self->_log('Exiting loop ... DONE') if DEBUG;
106              
107 12         29 $self->{_head} = undef;
108 12         25 $self->{_is_done} = 1;
109 12         20 last;
110             }
111             }
112              
113 95         306 return $self->{_head} = $next;
114             }
115              
116             1;
117              
118             __END__