File Coverage

blib/lib/App/Codeowners/Options.pm
Criterion Covered Total %
statement 73 144 50.6
branch 20 54 37.0
condition 4 35 11.4
subroutine 14 21 66.6
pod 3 11 27.2
total 114 265 43.0


line stmt bran cond sub pod time code
1             package App::Codeowners::Options;
2             # ABSTRACT: Getopt and shell completion for App::Codeowners
3              
4 1     1   12 use v5.10.1;
  1         3  
5 1     1   5 use warnings;
  1         2  
  1         24  
6 1     1   5 use strict;
  1         2  
  1         22  
7              
8 1     1   4 use Encode qw(decode);
  1         2  
  1         61  
9 1     1   722 use Getopt::Long 2.39 ();
  1         10366  
  1         52  
10 1     1   11 use Path::Tiny;
  1         2  
  1         440  
11              
12             our $VERSION = '0.49'; # VERSION
13              
14             sub pod2usage {
15 2     2 0 539 eval { require Pod::Usage };
  2         616  
16 2 50       39386 if ($@) {
17 0 0       0 my $ref = $VERSION eq '9999.999' ? 'master' : "v$VERSION";
18             my $exit = (@_ == 1 && $_[0] =~ /^\d+$/ && $_[0]) //
19 0   0     0 (@_ % 2 == 0 && {@_}->{'-exitval'}) // 2;
      0        
      0        
      0        
20 0         0 print STDERR <
21             Online documentation is available at:
22              
23             https://github.com/chazmcgarvey/git-codeowners/blob/$ref/README.md
24              
25             Tip: To enable inline documentation, install the Pod::Usage module.
26              
27             END
28 0         0 exit $exit;
29             }
30             else {
31 2         10 Pod::Usage::pod2usage(@_);
32             }
33             }
34              
35             sub early_options {
36             return {
37 7 50   7 0 237 'color|colour!' => (-t STDOUT ? 1 : 0), ## no critic (InputOutput::ProhibitInteractiveTest)
38             'format|f=s' => undef,
39             'help|h|?' => 0,
40             'manual|man' => 0,
41             'shell-completion:s' => undef,
42             'version|v' => 0,
43             };
44             }
45              
46             sub command_options {
47             return {
48 9     9 0 229 'create' => {},
49             'owners' => {
50             'pattern=s' => '',
51             },
52             'patterns' => {
53             'owner=s' => '',
54             },
55             'projects' => {},
56             'show' => {
57             'owner=s@' => [],
58             'pattern=s@' => [],
59             'project=s@' => [],
60             'patterns!' => 0,
61             'projects!' => undef,
62             },
63             'update' => {},
64             };
65             }
66              
67             sub commands {
68 0     0 0 0 my $self = shift;
69 0         0 my @commands = sort keys %{$self->command_options};
  0         0  
70 0         0 return @commands;
71             }
72              
73             sub options {
74 0     0 0 0 my $self = shift;
75 0         0 my @command_options;
76 0 0       0 if (my $command = $self->{command}) {
77 0 0       0 @command_options = keys %{$self->command_options->{$command} || {}};
  0         0  
78             }
79 0         0 return (keys %{$self->early_options}, @command_options);
  0         0  
80             }
81              
82             sub new {
83 7     7 0 45 my $class = shift;
84 7         42 my @args = @_;
85              
86             # assume UTF-8 args if non-ASCII
87 1 50   1   8 @args = map { decode('UTF-8', $_) } @args if grep { /\P{ASCII}/ } @args;
  1         2  
  1         15  
  7         25  
  0         0  
  15         118  
88              
89 7         56 my $self = bless {}, $class;
90              
91 7         30 my @args_copy = @args;
92              
93 7 50       87 my $opts = $self->get_options(
94             args => \@args,
95             spec => $self->early_options,
96             config => 'pass_through',
97             ) or pod2usage(2);
98              
99 7 50       43 if ($ENV{CODEOWNERS_COMPLETIONS}) {
100 0   0     0 $self->{command} = $args[0] || '';
101 0         0 my $cword = $ENV{CWORD};
102 0   0     0 my $cur = $ENV{CUR} || '';
103             # Adjust cword to remove progname
104 0         0 while (0 < --$cword) {
105 0 0 0     0 last if $cur eq ($args_copy[$cword] || '');
106             }
107 0         0 $self->completions($cword, @args_copy);
108 0         0 exit 0;
109             }
110              
111 7 100       29 if ($opts->{version}) {
112 1         7 my $progname = path($0)->basename;
113 1         128 print "${progname} ${VERSION}\n";
114 1         7 exit 0;
115             }
116 6 100       23 if ($opts->{help}) {
117 1         15 pod2usage(-exitval => 0, -verbose => 99, -sections => [qw(NAME SYNOPSIS OPTIONS COMMANDS)]);
118             }
119 5 50       18 if ($opts->{manual}) {
120 0         0 pod2usage(-exitval => 0, -verbose => 2);
121             }
122 5 50       17 if (defined $opts->{shell_completion}) {
123 0         0 $self->shell_completion($opts->{shell_completion});
124 0         0 exit 0;
125             }
126              
127             # figure out the command (or default to "show")
128 5         25 my $command = shift @args;
129 5   50     25 my $command_options = $self->command_options->{$command || ''};
130 5 50       40 if (!$command_options) {
131 0 0       0 unshift @args, $command if defined $command;
132 0         0 $command = 'show';
133 0         0 $command_options = $self->command_options->{$command};
134             }
135              
136 5 100       23 my $more_opts = $self->get_options(
137             args => \@args,
138             spec => $command_options,
139             ) or pod2usage(2);
140              
141 4         61 %$self = (%$opts, %$more_opts, command => $command, args => \@args);
142 4         48 return $self;
143             }
144              
145             sub command {
146 4     4 0 16 my $self = shift;
147 4         14 my $command = $self->{command};
148 4         8 my @commands = sort keys %{$self->command_options};
  4         11  
149 4 50       37 return if not grep { $_ eq $command } @commands;
  24         52  
150 4         26 $command =~ s/[^a-z]/_/g;
151 4         25 return $command;
152             }
153              
154             sub args {
155 4     4 0 14 my $self = shift;
156 4 50       7 return @{$self->{args} || []};
  4         45  
157             }
158              
159              
160             sub get_options {
161 12     12 1 54 my $self = shift;
162 12 50 33     152 my $args = {@_ == 1 && ref $_[0] eq 'HASH' ? %{$_[0]} : @_};
  0         0  
163              
164 12         35 my %options;
165             my %results;
166 12         24 while (my ($opt, $default_value) = each %{$args->{spec}}) {
  74         250  
167 62         311 my ($name) = $opt =~ /^([^=:!|]+)/;
168 62         181 $name =~ s/-/_/g;
169 62         196 $results{$name} = $default_value;
170 62         164 $options{$opt} = \$results{$name};
171             }
172              
173 12 50       102 if (my $fn = $args->{callback}) {
174             $options{'<>'} = sub {
175 0     0   0 my $arg = shift;
176 0         0 $fn->($arg, \%results);
177 0         0 };
178             }
179              
180 12         205 my $p = Getopt::Long::Parser->new;
181 12   100     759 $p->configure($args->{config} || 'default');
182 12 100       1002 return if !$p->getoptionsfromarray($args->{args}, %options);
183              
184 11         7481 return \%results;
185             }
186              
187              
188             sub shell_completion {
189 0     0 1   my $self = shift;
190 0   0       my $type = lc(shift || 'bash');
191              
192 0 0         if ($type eq 'bash') {
193 0           print <<'END';
194             # git-codeowners - Bash completion
195             # To use, eval this code:
196             # eval "$(git-codeowners --shell-completion)"
197             # This will work without the bash-completion package, but handling of colons
198             # in the completion word will work better with bash-completion installed and
199             # enabled.
200             _git_codeowners() {
201             local cur words cword
202             if declare -f _get_comp_words_by_ref >/dev/null
203             then
204             _get_comp_words_by_ref -n : cur cword words
205             else
206             words=("${COMP_WORDS[@]}")
207             cword=${COMP_CWORD}
208             cur=${words[cword]}
209             fi
210             local IFS=$'\n'
211             COMPREPLY=($(CODEOWNERS_COMPLETIONS=1 CWORD="$cword" CUR="$cur" ${words[@]}))
212             # COMPREPLY=($(${words[0]} --completions "$cword" "${words[@]}"))
213             if [[ "$?" -eq 9 ]]
214             then
215             COMPREPLY=($(compgen -A "${COMPREPLY[0]}" -- "$cur"))
216             fi
217             declare -f __ltrim_colon_completions >/dev/null && \
218             __ltrim_colon_completions "$cur"
219             return 0
220             }
221             complete -F _git_codeowners git-codeowners
222             END
223             }
224             else {
225             # TODO - Would be nice to support Zsh
226 0           warn "No such shell completion: $type\n";
227             }
228             }
229              
230              
231             sub completions {
232 0     0 1   my $self = shift;
233 0           my $cword = shift;
234 0           my @words = @_;
235              
236 0   0       my $current = $words[$cword] || '';
237 0   0       my $prev = $words[$cword - 1] || '';
238              
239 0           my $reply;
240              
241 0 0 0       if ($prev eq '--format' || $prev eq '-f') {
    0          
242 0           $reply = $self->_completion_formats;
243             }
244             elsif ($current =~ /^-/) {
245 0           $reply = $self->_completion_options;
246             }
247             else {
248 0 0         if (!$self->command) {
249 0           $reply = [$self->commands, @{$self->_completion_options([keys %{$self->early_options}])}];
  0            
  0            
250             }
251             else {
252 0           print 'file';
253 0           exit 9;
254             }
255             }
256              
257 0           local $, = "\n";
258 0           print grep { /^\Q$current\E/ } @$reply;
  0            
259 0           exit 0;
260             }
261              
262             sub _completion_options {
263 0     0     my $self = shift;
264 0   0       my $opts = shift || [$self->options];
265              
266 0           my @options;
267              
268 0           for my $option (@$opts) {
269 0           my ($names, $op, $vtype) = $option =~ /^([^=:!]+)([=:!]?)(.*)$/;
270 0           my @names = split(/\|/, $names);
271              
272 0           for my $name (@names) {
273 0 0         if ($op eq '!') {
274 0           push @options, "--$name", "--no-$name";
275             }
276             else {
277 0 0         if (length($name) > 1) {
278 0           push @options, "--$name";
279             }
280             else {
281 0           push @options, "-$name";
282             }
283             }
284             }
285             }
286              
287 0           return [sort @options];
288             }
289              
290 0     0     sub _completion_formats { [qw(csv json json:pretty tsv yaml)] }
291              
292             1;
293              
294             __END__