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 7 7 100.0
total 99 110 90.0


line stmt bran cond sub pod time code
1             package Directory::Scanner::StreamBuilder::Recursive;
2             # ABSTRACT: Recrusive streaming directory iterator
3              
4 8     8   48 use strict;
  8         25  
  8         200  
5 8     8   37 use warnings;
  8         15  
  8         168  
6              
7 8     8   36 use Carp ();
  8         14  
  8         94  
8 8     8   34 use Scalar::Util ();
  8         15  
  8         111  
9              
10 8     8   37 use UNIVERSAL::Object;
  8         16  
  8         152  
11 8     8   33 use Directory::Scanner::API::Stream;
  8         18  
  8         377  
12              
13             our $VERSION = '0.02';
14             our $AUTHORITY = 'cpan:STEVAN';
15              
16 8   50 8   36 use constant DEBUG => $ENV{DIR_SCANNER_STREAM_RECURSIVE_DEBUG} // 0;
  8         14  
  8         568  
17              
18             ## ...
19              
20 8     8   722 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         179 _stack => sub { [] },
27 12         234 _is_done => sub { 0 },
28 12         81 _is_closed => sub { 0 },
29             )
30 8     8   3066 }
31              
32             ## ...
33              
34             sub BUILD {
35 12     12 1 353 my ($self, $params) = @_;
36              
37 12         40 my $stream = $self->{stream};
38              
39 12 50 33     150 (Scalar::Util::blessed($stream) && $stream->DOES('Directory::Scanner::API::Stream'))
40             || Carp::confess 'You must supply a directory stream';
41              
42 12         38 push @{$self->{_stack}} => $stream;
  12         42  
43             }
44              
45             sub clone {
46 0     0 1 0 my ($self, $dir) = @_;
47 0         0 return $self->new( stream => $self->{stream}->clone( $dir ) );
48             }
49              
50             ## accessor
51              
52 44     44 1 2695 sub head { $_[0]->{_head} }
53              
54 39     39 1 10067 sub is_done { $_[0]->{_is_done} }
55 33     33 1 2250 sub is_closed { $_[0]->{_is_closed} }
56              
57             sub close {
58 12     12 1 389 my $self = $_[0];
59 12         25 while ( my $stream = pop @{ $self->{_stack} } ) {
  12         53  
60 0         0 $stream->close;
61             }
62 12         29 $self->{_is_closed} = 1;
63 12         30 return;
64             }
65              
66             sub next {
67 95     95 1 7104 my $self = $_[0];
68              
69 95 50       202 return if $self->{_is_done};
70              
71             Carp::confess 'Cannot call `next` on a closed stream'
72 95 50       187 if $self->{_is_closed};
73              
74 95         130 my $next;
75 95         131 while (1) {
76 148         508 undef $next; # clear any previous values, just cause ...
77 148         201 $self->_log('Entering loop ... ') if DEBUG;
78              
79 148 100       309 if ( my $current = $self->{_stack}->[-1] ) {
80 136         162 $self->_log('Stream available in stack') if DEBUG;
81 136 100       300 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       210 if ( $candidate->is_dir ) {
86 41         373 push @{$self->{_stack}} => $current->clone( $candidate );
  41         138  
87             }
88              
89             # return our successful candidate
90 83         569 $next = $candidate;
91 83         147 last;
92             }
93             else {
94 53         69 $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         96 my $old = pop @{$self->{_stack}};
  53         97  
99 53 50       189 $old->close unless $old->is_closed;
100 53         201 next;
101             }
102             }
103             else {
104 12         26 $self->_log('No more streams available in stack') if DEBUG;
105 12         20 $self->_log('Exiting loop ... DONE') if DEBUG;
106              
107 12         33 $self->{_head} = undef;
108 12         25 $self->{_is_done} = 1;
109 12         21 last;
110             }
111             }
112              
113 95         261 return $self->{_head} = $next;
114             }
115              
116             1;
117              
118             __END__