File Coverage

blib/lib/Dallycot/Value/Stream.pm
Criterion Covered Total %
statement 21 124 16.9
branch 0 22 0.0
condition 0 3 0.0
subroutine 7 31 22.5
pod 0 13 0.0
total 28 193 14.5


line stmt bran cond sub pod time code
1             package Dallycot::Value::Stream;
2             our $AUTHORITY = 'cpan:JSMITH';
3              
4             # ABSTRACT: A linked list of values with a possible generator
5              
6 23     23   14611 use strict;
  23         39  
  23         764  
7 23     23   98 use warnings;
  23         49  
  23         505  
8              
9             # RDF List
10 23     23   79 use utf8;
  23         29  
  23         102  
11 23     23   11912 use Readonly;
  23         58379  
  23         1993  
12              
13             Readonly my $HEAD => 0;
14             Readonly my $TAIL => 1;
15             Readonly my $TAIL_PROMISE => 2;
16              
17 23     23   146 use parent 'Dallycot::Value::Collection';
  23         30  
  23         140  
18              
19 23     23   926 use experimental qw(switch);
  23         35  
  23         159  
20              
21 23     23   3028 use Promises qw(deferred);
  23         1516  
  23         92  
22              
23             sub new {
24 0     0 0   my ( $class, $head, $tail, $promise ) = @_;
25 0   0       $class = ref $class || $class;
26 0           return bless [ $head, $tail, $promise ] => $class;
27             }
28              
29 0     0 0   sub is_defined { return 1 }
30              
31 0     0 0   sub is_empty {return}
32              
33             sub to_rdf {
34 0     0 0   my($self, $model) = @_;
35              
36 0           my @things;
37 0           my $root = $self;
38 0           push @things, $root -> [0]->to_rdf($model);
39 0           while($root -> [1]) {
40 0           $root = $root->[1];
41 0           push @things, $root->[0]->to_rdf($model);
42             }
43 0 0         if($root -> [2]) {
44 0           return $model -> list_with_promise(@things, $root->[2]);
45             }
46             else {
47 0           return $model -> list(@things);
48             }
49             }
50              
51             sub prepend {
52 0     0 0   my ( $self, @things ) = @_;
53              
54 0           my $stream = $self;
55              
56 0           foreach my $thing (@things) {
57 0           $stream = __PACKAGE__->new( $thing, $stream );
58             }
59 0           return $stream;
60             }
61              
62             sub as_text {
63 0     0 0   my ($self) = @_;
64              
65 0           my $text = "[ ";
66 0           my $point = $self;
67 0           $text .= $point->[$HEAD]->as_text;
68 0           while ( defined $point->[$TAIL] ) {
69 0           $point = $point->[$TAIL];
70 0 0         if ( defined $point->[$HEAD] ) {
71 0           $text .= ", ";
72 0           $text .= $point->[$HEAD]->as_text;
73             }
74             }
75 0 0         if ( defined $point->[$TAIL_PROMISE] ) {
76 0           $text .= ", ...";
77             }
78 0           return $text . " ]";
79             }
80              
81             sub calculate_length {
82 0     0 0   my ( $self, $engine ) = @_;
83              
84 0           my $d = deferred;
85              
86 0           my $ptr = $self;
87              
88 0           my $count = 1;
89              
90 0           while ( $ptr->[$TAIL] ) {
91 0           $count++;
92 0           $ptr = $ptr->[$TAIL];
93             }
94              
95 0 0         if ( $ptr->[$TAIL_PROMISE] ) {
96 0           $d->resolve( Dallycot::Value::Numeric->new( Math::BigRat->binf() ) );
97             }
98             else {
99 0           $d->resolve( Dallycot::Value::Numeric->new($count) );
100             }
101              
102 0           return $d->promise;
103             }
104              
105             sub _resolve_tail_promise {
106 0     0     my ( $self, $engine ) = @_;
107              
108             return $self->[$TAIL_PROMISE]->apply( $engine, {} )->then(
109             sub {
110 0     0     my ($list_tail) = @_;
111 0           given ( ref $list_tail ) {
112 0           when (__PACKAGE__) {
113 0           $self->[$TAIL] = $list_tail;
114 0           $self->[$TAIL_PROMISE] = undef;
115             }
116 0           when ('Dallycot::Value::Vector') {
117              
118             # convert finite vector into linked list
119 0           my @values = @$list_tail;
120 0           my $point = $self;
121 0           while (@values) {
122 0           $point->[$TAIL] = $self->new( shift @values );
123 0           $point = $point->[$TAIL];
124             }
125             }
126 0           default {
127 0           $self->[$TAIL] = $list_tail;
128 0           $self->[$TAIL_PROMISE] = undef;
129             }
130             }
131             }
132 0           );
133             }
134              
135             sub apply_map {
136 0     0 0   my ( $self, $engine, $transform ) = @_;
137              
138             return $engine->make_map($transform)->then(
139             sub {
140 0     0     my ($map_t) = @_;
141              
142 0           $map_t->apply( $engine, {}, $self );
143             }
144 0           );
145             }
146              
147             sub apply_filter {
148 0     0 0   my ( $self, $engine, $filter ) = @_;
149              
150             return $engine->make_filter($filter)->then(
151             sub {
152 0     0     my ($filter_t) = @_;
153              
154 0           $filter_t->apply( $engine, {}, $self );
155             }
156 0           );
157             }
158              
159             sub drop {
160 0     0 0   my ( $self, $engine ) = @_;
161              
162 0           return;
163             }
164              
165             sub value_at {
166 0     0 0   my ( $self, $engine, $index ) = @_;
167              
168 0 0         if ( $index == 1 ) {
169 0           return $self->head($engine);
170             }
171              
172 0           my $d = deferred;
173              
174 0 0         if ( $index < 1 ) {
175 0           $d->resolve( $engine->UNDEFINED );
176             }
177             else {
178             # we want to keep resolving tails until we get somewhere
179 0           $self->_walk_tail( $engine, $d, $index - 1 );
180             }
181              
182 0           return $d->promise;
183             }
184              
185             sub _walk_tail {
186 0     0     my ( $self, $engine, $d, $count ) = @_;
187              
188 0 0         if ( $count > 0 ) {
189             $self->tail($engine)->done(
190             sub {
191 0     0     my ($tail) = @_;
192 0           $tail->_walk_tail( $engine, $d, $count - 1 );
193             },
194             sub {
195 0     0     $d->reject(@_);
196             }
197 0           );
198             }
199             else {
200             $self->head($engine)->done(
201             sub {
202 0     0     $d -> resolve(@_);
203             },
204             sub {
205 0     0     $d -> reject(@_);
206             }
207 0           );
208             }
209             }
210              
211             sub head {
212 0     0 0   my ( $self, $engine ) = @_;
213              
214 0           my $p = deferred;
215              
216 0 0         if ( defined $self->[$HEAD] ) {
217 0           $p->resolve( $self->[0] );
218             }
219             else {
220 0           $p->resolve( bless [] => 'Dallycot::Value::Undefined' );
221             }
222              
223 0           return $p->promise;
224             }
225              
226             sub tail {
227 0     0 0   my ( $self, $engine ) = @_;
228              
229 0           my $p = deferred;
230              
231 0 0         if ( defined $self->[$TAIL] ) {
    0          
232 0           $p->resolve( $self->[$TAIL] );
233             }
234             elsif ( defined $self->[$TAIL_PROMISE] ) {
235             $self->_resolve_tail_promise($engine)->done(
236             sub {
237 0 0   0     if ( defined $self->[$TAIL] ) {
238 0           $p->resolve( $self->[$TAIL] );
239             }
240             else {
241 0           $p->reject('The tail operator expects a stream-like object.');
242             }
243             },
244             sub {
245 0     0     $p->reject(@_);
246             }
247 0           );
248             }
249             else {
250 0           $p->resolve( bless [] => 'Dallycot::Value::EmptyStream' );
251             }
252              
253 0           return $p->promise;
254             }
255              
256             1;