File Coverage

blib/lib/DTL/Fast/Filter/Slice.pm
Criterion Covered Total %
statement 62 66 93.9
branch 30 34 88.2
condition 8 13 61.5
subroutine 12 12 100.0
pod 0 7 0.0
total 112 132 84.8


line stmt bran cond sub pod time code
1             package DTL::Fast::Filter::Slice;
2 3     3   1329 use strict;
  3         7  
  3         77  
3 3     3   14 use utf8;
  3         4  
  3         15  
4 3     3   70 use warnings FATAL => 'all';
  3         5  
  3         103  
5 3     3   13 use parent 'DTL::Fast::Filter';
  3         5  
  3         16  
6              
7             $DTL::Fast::FILTER_HANDLERS{slice} = __PACKAGE__;
8              
9 3     3   197 use Scalar::Util qw(reftype);
  3         6  
  3         2073  
10              
11             #@Override
12             sub parse_parameters
13             {
14 24     24 0 42 my $self = shift;
15             die $self->get_parse_error("no slicing settings specified")
16 24 100       29 if (not scalar @{$self->{parameter}});
  24         66  
17 23         43 $self->{settings} = $self->{parameter}->[0];
18 23         61 return $self;
19             }
20              
21             #@Override
22             sub filter
23             {
24 26     26 0 49 my ($self, $filter_manager, $value, $context) = @_;
25              
26 26         67 my $settings = $self->{settings}->render($context);
27              
28 26 100       69 die $self->get_render_error( $context, 'slicing format is not defined in current context')
29             if (not defined $settings);
30              
31 25         62 my $value_type = reftype $value;
32              
33 25 100       64 if (not defined $value_type)
    100          
    100          
    50          
34             {
35 12 100       26 if (defined $value)
36             {
37 11         22 eval {
38 11         30 $value = $self->slice_scalar($value, $settings);
39             };
40 11 100       34 die $self->get_render_error($context, $@) if ($@);
41             }
42             else
43             {
44 1         8 die $self->get_render_error( $context, 'unable to slice undef value');
45             }
46             }
47             elsif ($value_type eq 'ARRAY')
48             {
49             eval
50 11         20 {
51 11         22 $value = $self->slice_array($value, $settings);
52             };
53 11 50       25 die $self->get_render_error($context, $@) if ($@);
54             }
55             elsif ($value_type eq 'HASH')
56             {
57 1         5 $value = $self->slice_hash($value, $settings);
58             }
59             elsif ($value_type eq 'SCALAR')
60             {
61 0         0 $value = $self->slice_scalar($$value, $settings);
62             }
63             else
64             {
65 1   50     18 die $self->get_render_error(
66             $context
67             , sprintf(
68             "can slice only HASH, ARRAY or SCALAR values, not %s (%s)"
69             , $value_type
70             , ref $value || 'SCALAR'
71             )
72             );
73             }
74              
75 22         66 return $value;
76             }
77              
78             sub slice_scalar
79             {
80 11     11 0 25 my ($self, $scalar, $settings ) = @_;
81              
82 11         30 my ($start, $end) = $self->parse_indexes($settings, length($scalar) - 1 );
83              
84 10         34 return substr $scalar, $start, $end - $start + 1;
85             }
86              
87             sub slice_array
88             {
89 11     11 0 20 my ($self, $array, $settings ) = @_;
90              
91 11         24 my ($start, $end) = $self->parse_indexes($settings, $#$array);
92              
93 11         28 return [ @{$array}[$start .. $end] ];
  11         39  
94             }
95              
96              
97             sub slice_hash
98             {
99 1     1 0 3 my ($self, $hash, $settings) = @_;
100 1         7 return [ @{$hash}{(split /\s*,\s*/, $settings)} ];
  1         7  
101             }
102              
103             sub parse_indexes
104             {
105 22     22 0 51 my ($self, $settings, $last_index) = @_;
106              
107 22         36 my $start = 0;
108 22         29 my $end;
109              
110 22 100       133 if ($settings =~ /^([-\d]+)?\:([-\d]+)?$/) # python's format
    100          
111             {
112 12   66     29 $start = $self->python_index_map($1, $last_index) // $start;
113 12 100       36 $end = defined $2 ?
114             $self->python_index_map($2, $last_index) - 1
115             : $last_index;
116             }
117             elsif ($settings =~ /^([-\d]+)?\s*\.\.\s*([-\d]+)?$/) # perl's format
118             {
119 9   66     37 $start = $1 // $start;
120 9   66     30 $end = $2 // $last_index;
121             }
122             else
123             {
124 1   50     9 die sprintf(
125             "array slicing option may be specified in one of the following formats:\npython: [from_index]:[to_index+1]\n perl: [from_index]..[to_index]\ngot `%s` instead.\n"
126             , $settings // undef
127             );
128             }
129              
130 21 50       52 $start = $last_index if ($start > $last_index);
131 21 100       44 $end = $last_index if ($end > $last_index);
132              
133 21 50       44 if ($start > $end) {
134 0         0 my $var = $start;
135 0         0 $start = $end;
136 0         0 $end = $var;
137             }
138              
139 21         50 return ($start, $end);
140             }
141              
142             sub python_index_map
143             {
144 19     19 0 46 my ( $self, $pyvalue, $lastindex ) = @_;
145              
146 19 100       57 return $pyvalue if (not defined $pyvalue);
147              
148 14 100       51 return $pyvalue < 0 ?
149             $lastindex + $pyvalue + 1
150             : $pyvalue;
151             }
152              
153             1;