File Coverage

blib/lib/MooseX/App/Plugin/Term/Meta/Attribute.pm
Criterion Covered Total %
statement 23 171 13.4
branch 0 76 0.0
condition 0 27 0.0
subroutine 8 15 53.3
pod 0 5 0.0
total 31 294 10.5


line stmt bran cond sub pod time code
1             # ============================================================================
2             package MooseX::App::Plugin::Term::Meta::Attribute;
3             # ============================================================================
4              
5 1     1   1097 use utf8;
  1         3  
  1         6  
6 1     1   51 use 5.010;
  1         5  
7              
8 1     1   7 use namespace::autoclean;
  1         2  
  1         6  
9 1     1   75 use Moose::Role;
  1         3  
  1         5  
10              
11 1     1   5584 no if $] >= 5.018000, warnings => qw(experimental::smartmatch);
  1         2  
  1         10  
12              
13 1     1   103 use Term::ReadKey;
  1         3  
  1         2008  
14              
15             has 'cmd_term' => (
16             is => 'ro',
17             isa => 'Bool',
18             default => sub {0},
19             );
20              
21             has 'cmd_term_label' => (
22             is => 'ro',
23             isa => 'Str',
24             predicate => 'has_cmd_term_label',
25             );
26              
27             sub cmd_term_label_full {
28 0     0 0   my ($self) = @_;
29              
30 0           my $label = $self->cmd_term_label_name;
31 0           my @tags;
32 0 0         if ($self->is_required) {
33 0           push(@tags,'Required');
34             } else {
35 0           push(@tags,'Optional');
36             }
37              
38 0 0         if ($self->has_type_constraint) {
39 0           my $type_constraint = $self->type_constraint;
40 0 0         if ($type_constraint->is_a_type_of('Bool')) {
41 0           push(@tags,'Y/N');
42             } else {
43 0           push(@tags,$self->cmd_type_constraint_description($type_constraint));
44             }
45             }
46 0 0         if (scalar @tags) {
47 0           $label .= ' ('.join(', ',@tags).')';
48             }
49              
50 0           return $label;
51             }
52              
53             sub cmd_term_label_name {
54 0     0 0   my ($self) = @_;
55              
56 0           my $label;
57 0 0         if ($self->has_cmd_term_label) {
    0          
58 0           return $self->cmd_term_label;
59             } elsif ($self->has_documentation) {
60 0           return $self->documentation;
61             } else {
62 0           return $self->name;
63             }
64             }
65              
66             sub cmd_term_read {
67 0     0 0   my ($self) = @_;
68              
69 0 0 0       if ($self->has_type_constraint
70             && $self->type_constraint->is_a_type_of('Bool')) {
71 0           return $self->cmd_term_read_bool();
72             } else {
73 0           return $self->cmd_term_read_string();
74             }
75             }
76              
77             sub cmd_term_read_string {
78 0     0 0   my ($self) = @_;
79              
80 0           my $label = $self->cmd_term_label_full;
81 0           my ($return,@history,$history_disable,$allowed);
82              
83 0           binmode STDIN,':encoding(UTF-8)';
84              
85             # Prefill history with enums
86 0 0         if ($self->has_type_constraint) {
87 0           my $type_constraint = $self->type_constraint;
88 0 0         if ($type_constraint->isa('Moose::Meta::TypeConstraint::Enum')) {
    0          
89 0           push(@history,@{$self->type_constraint->values});
  0            
90 0           $history_disable = 1
91             } elsif (!$type_constraint->has_coercion) {
92 0 0         if ($type_constraint->is_a_type_of('Int')) {
    0          
93 0           $allowed = qr/[0-9]/;
94             } elsif ($type_constraint->is_a_type_of('Num')) {
95 0           $allowed = qr/[0-9.]/;
96             }
97             }
98             }
99              
100 0 0         push(@history,"")
101             unless scalar @history;
102              
103 0           my $history_index = 0;
104             my $history_add = sub {
105 0     0     my $entry = shift;
106 0 0 0       if (! $history_disable
      0        
      0        
107             && defined $entry
108             && $entry !~ m/^\s*$/
109             && ! ($entry ~~ \@history)) {
110 0           push(@history,$entry);
111             }
112 0           };
113              
114 0           ReadMode('cbreak'); # change input mode
115             TRY_STRING:
116 0           while (1) {
117 0 0 0       print "\n"
118             if defined $return
119             && $return !~ /^\s*$/;
120 0           $return = '';
121              
122 0 0         if (defined $Term::ANSIColor::VERSION) {
123 0           say Term::ANSIColor::color('white bold').$label.' :'.Term::ANSIColor::color('reset');
124             } else {
125 0           say $label.": ";
126             }
127              
128 0           1 while defined ReadKey -1; # discard any previous input
129              
130 0           my $cursor = 0;
131              
132             KEY_STRING:
133 0           while (1) {
134 0           my $key = ReadKey 0; # read a single character
135 0           my $length = length($return);
136 0           my $key_code = ord($key);
137              
138 0 0         if ($key_code == 10) { # Enter
    0          
    0          
139 0           print "\n";
140 0           my $error;
141 0 0         if ($return =~ m/^\s*$/) {
142 0 0         if ($self->is_required) {
143 0           $error = 'Value is required';
144             } else {
145 0           $return = undef;
146 0           last TRY_STRING;
147             }
148             } else {
149 0           $error = $self->cmd_type_constraint_check($return);
150             }
151 0 0         if ($error) {
152 0 0         if (defined $Term::ANSIColor::VERSION) {
153 0           say Term::ANSIColor::color('bright_red bold').$error.Term::ANSIColor::color('reset');
154             } else {
155 0           say $error;
156             }
157 0           $history_add->($return);
158 0           next TRY_STRING;
159             } else {
160 0           last TRY_STRING;
161             }
162             } elsif ($key_code == 27) { # Escape sequence
163 0           my $escape;
164 0           while (1) { # Read rest of escape sequence
165 0           my $code = ReadKey -1;
166 0 0         last unless defined $code;
167 0           $escape .= $code;
168             }
169 0 0         if (defined $escape) {
170 0           given ($escape) {
171 0           when ('[D') { # Cursor left
172 0 0         if ($cursor > 0) {
173 0           print "\b";
174 0           $cursor--;
175             }
176             }
177 0           when ($escape eq '[C') { # Cursor right
178 0 0         if ($cursor < length($return)) {
179 0           print substr $return,$cursor,1;
180 0           $cursor++;
181             }
182             }
183 0           when ($escape eq '[A') { # Cursor up
184 0           $history_add->($return);
185 0           print "\b" x $cursor;
186 0           print " " x length($return);
187 0           print "\b" x length($return);
188              
189 0 0 0       $history_index ++
190             if defined $history[$history_index]
191             && $history[$history_index] eq $return;
192 0 0         $history_index = 0
193             unless defined $history[$history_index];
194              
195 0           $return = $history[$history_index];
196 0           $cursor = length($return);
197 0           print $return;
198 0           $history_index++;
199             }
200 0           when ($escape eq '[3~') { # Del
201 0 0         if ($cursor != length($return)) {
202 0           substr $return,$cursor,1,'';
203 0           print substr $return,$cursor;
204 0           print " ".(("\b") x (length($return) - $cursor + 1));
205             }
206             }
207 0           when ($escape eq 'OH') { # Pos 1
208 0           print (("\b") x $cursor);
209 0           $cursor = 0;
210             }
211 0           when ($escape eq 'OF') { # End
212 0           print substr $return,$cursor;
213 0           $cursor = length($return);
214             }
215             #default {
216             # print $escape;
217             #}
218             }
219             } else {
220 0           $history_add->($return);
221 0           next TRY_STRING;
222             }
223              
224             } elsif ($key_code == 127) { # Backspace
225 0 0         if ($cursor == 0) { # Ignore first
226 0           next KEY_STRING;
227             }
228 0           $cursor--;
229 0           substr $return,$cursor,1,''; # string
230 0           print "\b".substr $return,$cursor; # print
231 0           print " ".(("\b") x (length($return) - $cursor + 1)); # cursor
232             } else { # Character
233 0 0 0       if ($_ <= 31) { # ignore controll chars
    0          
234 0           print "\a";
235 0           next KEY_STRING;
236             } elsif (defined $allowed
237             && $key !~ /$allowed/) {
238 0           print "\a";
239 0           next KEY_STRING;
240             }
241 0           substr $return,$cursor,0,$key; # string
242 0           print substr $return,$cursor; # print
243 0           $cursor++;
244 0           print (("\b") x (length($return) - $cursor)); # cursor
245             }
246             }
247             }
248 0           ReadMode 0;
249              
250 0           return $return;
251             }
252              
253             sub cmd_term_read_bool {
254 0     0 0   my ($self) = @_;
255              
256 0           my $label = $self->cmd_term_label_full;
257 0           my $return;
258              
259 0 0         if (defined $Term::ANSIColor::VERSION) {
260 0           say Term::ANSIColor::color('white bold').$label.' :'.Term::ANSIColor::color('reset');
261             } else {
262 0           say $label.": ";
263             }
264 0           ReadMode 4; # change to raw input mode
265             TRY:
266 0           while (1) {
267 0           1 while defined ReadKey -1; # discard any previous input
268 0           my $key = ReadKey 0; # read a single character
269 0 0 0       if ($key =~ /^[yn]$/i) {
    0 0        
    0          
270 0           say uc($key);
271 0 0         $return = uc($key) eq 'Y' ? 1:0;
272 0           last;
273             } elsif ((ord($key) == 10 || ord($key) == 27) && ! $self->is_required) {
274 0           last;
275             } elsif (ord($key) == 3) {
276 0           ReadMode 0;
277 0           kill INT => $$; # Not sure ?
278             }
279             }
280 0           ReadMode 0;
281              
282 0           return $return;
283             }
284              
285             around 'cmd_tags_list' => sub {
286             my $orig = shift;
287             my ($self) = @_;
288              
289             my @tags = $self->$orig();
290              
291             push(@tags,'Term')
292             if $self->can('cmd_term')
293             && $self->cmd_term;
294              
295             return @tags;
296             };
297              
298             {
299             package Moose::Meta::Attribute::Custom::Trait::AppTerm;
300              
301 1     1   10 use strict;
  1         3  
  1         44  
302 1     1   7 use warnings;
  1         3  
  1         74  
303              
304 0     0     sub register_implementation { return 'MooseX::App::Plugin::Term::Meta::Attribute' }
305             }
306              
307             1;