File Coverage

blib/lib/Module/List/More.pm
Criterion Covered Total %
statement 91 105 86.6
branch 52 76 68.4
condition 46 70 65.7
subroutine 3 3 100.0
pod 0 1 0.0
total 192 255 75.2


line stmt bran cond sub pod time code
1             ## no critic: TestingAndDebugging::RequireUseStrict
2             package Module::List::More;
3              
4             #IFUNBUILT
5             # # use strict 'subs', 'vars';
6             # # use warnings;
7             #END IFUNBUILT
8              
9             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
10             our $DATE = '2022-08-12'; # DATE
11             our $DIST = 'Module-List-More'; # DIST
12             our $VERSION = '0.004011'; # VERSION
13              
14             # do our own exporting to start faster
15             sub import {
16 1     1   6 my $pkg = shift;
17 1         2 my $caller = caller;
18 1         3 for my $sym (@_) {
19 1 50       3 if ($sym eq 'list_modules') { *{"$caller\::$sym"} = \&{$sym} }
  1         2  
  1         2471  
  1         2  
20 0         0 else { die "$sym is not exported!" }
21             }
22             }
23              
24             sub list_modules($$) {
25 17     17 0 112952 my($prefix, $options) = @_;
26 17         28 my $trivial_syntax = $options->{trivial_syntax};
27 17         38 my($root_leaf_rx, $root_notleaf_rx);
28 17         0 my($notroot_leaf_rx, $notroot_notleaf_rx);
29 17 50       34 if($trivial_syntax) {
30 0         0 $root_leaf_rx = $notroot_leaf_rx = qr#:?(?:[^/:]+:)*[^/:]+:?#;
31 0         0 $root_notleaf_rx = $notroot_notleaf_rx =
32             qr#:?(?:[^/:]+:)*[^/:]+#;
33             } else {
34 17         60 $root_leaf_rx = $root_notleaf_rx = qr/[a-zA-Z_][0-9a-zA-Z_]*/;
35 17         38 $notroot_leaf_rx = $notroot_notleaf_rx = qr/[0-9a-zA-Z_]+/;
36             }
37              
38 17         23 my $recurse = $options->{recurse};
39              
40             # filter by wildcard. we cannot do this sooner because wildcard can be put
41             # at the end or at the beginning (e.g. '*::Path') so we still need
42 17         18 my $re_wildcard;
43 17 100 66     58 if ($options->{wildcard} || $options->{ls_mode}) {
44 8         40 require String::Wildcard::Bash;
45 8         15 my $orig_prefix = $prefix;
46             #print "DEBUG: orig_prefix = <$orig_prefix>\n";
47 8         21 my @prefix_parts = split /::/, $prefix;
48 8 0 33     32 pop @prefix_parts if $options->{ls_mode} && @prefix_parts && $orig_prefix !~ /::\z/
      33        
      0        
49             && !String::Wildcard::Bash::contains_wildcard($orig_prefix);
50 8         12 $prefix = "";
51 8         14 my $has_wildcard;
52 8         19 while (defined(my $part = shift @prefix_parts)) {
53 8 50       23 if (String::Wildcard::Bash::contains_wildcard($part)) {
54 8         245 $has_wildcard++;
55             # XXX limit recurse level to scalar(@prefix_parts), or -1 if has_globstar
56 8 100       17 $recurse = 1 if @prefix_parts;
57 8         16 last;
58             } else {
59 0         0 $prefix .= "$part\::";
60             }
61             }
62             #print "DEBUG: has_wildcard = $has_wildcard\n";
63 8 50 33     34 if ($options->{wildcard} && $has_wildcard) {
64 8         32 $re_wildcard = String::Wildcard::Bash::convert_wildcard_to_re({path_separator=>':', dotglob=>1, globstar=>1}, $orig_prefix);
65 8         727 $re_wildcard = qr/\A(?:$re_wildcard)\z/;
66             } else {
67 0 0       0 $re_wildcard = $orig_prefix =~ /::\z/ ? qr/\A\Q$orig_prefix\E/ : qr/\A\Q$orig_prefix\E(?:\z|::)/;
68             }
69             #print "DEBUG: re_wildcard = $re_wildcard\n";
70 8 100       26 $recurse = 1 if String::Wildcard::Bash::contains_globstar_wildcard($orig_prefix);
71             #print "DEBUG: recurse = $recurse\n";
72             }
73             #print "DEBUG: prefix = <$prefix>\n";
74              
75 17 50 33     422 die "bad module name prefix `$prefix'"
76             unless $prefix =~ /\A(?:${root_notleaf_rx}::
77             (?:${notroot_notleaf_rx}::)*)?\z/x &&
78             $prefix !~ /(?:\A|[^:]::)\.\.?::/;
79              
80 17         53 my $list_modules = $options->{list_modules};
81 17         22 my $list_prefixes = $options->{list_prefixes};
82 17         21 my $list_pod = $options->{list_pod};
83 17         19 my $use_pod_dir = $options->{use_pod_dir};
84 17 50 66     42 return {} unless $list_modules || $list_prefixes || $list_pod;
      33        
85 17         20 my $return_path = $options->{return_path};
86 17         19 my $return_library_path = $options->{return_library_path};
87 17         19 my $return_version = $options->{return_version};
88 17         20 my $all = $options->{all};
89 17         33 my @prefixes = ($prefix);
90 17         23 my %seen_prefixes;
91             my %results;
92             my $_set_or_add_result = sub {
93 101     101   176 my ($key, $result_field, $val, $always_all) = @_;
94 101 100 100     241 if (!$result_field) {
    100          
95 49   100     158 $results{$key} ||= undef;
96             } elsif ($all || $always_all) {
97 25   100     90 $results{$key}{$result_field} ||= [];
98 25         31 push @{ $results{$key}{$result_field} }, $val;
  25         112  
99             } else {
100             $results{$key}{$result_field} = $val
101 27 100       80 unless exists $results{$key}{$result_field};
102             }
103 17         65 };
104             #use DD; dd \@prefixes;
105 17         38 while(@prefixes) {
106 45         127 my $prefix = pop(@prefixes);
107 45         117 my @dir_suffix = split(/::/, $prefix);
108 45 100       100 my $module_rx =
109             $prefix eq "" ? $root_leaf_rx : $notroot_leaf_rx;
110 45         480 my $pm_rx = qr/\A($module_rx)\.pmc?\z/;
111 45         310 my $pod_rx = qr/\A($module_rx)\.pod\z/;
112 45 100       102 my $dir_rx =
113             $prefix eq "" ? $root_notleaf_rx : $notroot_notleaf_rx;
114 45         221 $dir_rx = qr/\A$dir_rx\z/;
115 45         91 foreach my $incdir (@INC) {
116 90         224 my $dir = join("/", $incdir, @dir_suffix);
117 90 100       1774 opendir(my $dh, $dir) or next;
118 62         890 while(defined(my $entry = readdir($dh))) {
119 261 100 100     2895 if(($list_modules && $entry =~ $pm_rx) ||
    100 33        
      66        
      100        
      100        
      100        
      100        
      66        
120             ($list_pod &&
121             $entry =~ $pod_rx)) {
122 60         158 my $key = $prefix.$1;
123             #print "DEBUG: key=<$key>\n";
124 60 100 100     337 next if $re_wildcard && $key !~ $re_wildcard;
125 33         71 my $path = "$dir/$entry";
126 33         87 $_set_or_add_result->($key);
127 33 100       79 $_set_or_add_result->($key, 'module_path', $path) if $return_path;
128 33 100       61 $_set_or_add_result->($key, 'library_path', $incdir) if $return_library_path;
129 33 100       150 if ($return_version) {
130 3         17 require ExtUtils::MakeMaker;
131 3         27 my $v = MM->parse_version($path);
132 3 100       707 $v = undef if $v eq 'undef';
133 3         11 $_set_or_add_result->($key, 'module_version', $v);
134             }
135             } elsif(($list_prefixes || $recurse) &&
136             ($entry ne '.' && $entry ne '..') &&
137             $entry =~ $dir_rx &&
138             -d join("/", $dir,
139             $entry)) {
140 40         120 my $newmod = $prefix.$entry;
141 40         63 my $newpfx = $newmod."::";
142 40 50       73 next if exists $seen_prefixes{$newpfx};
143 40 100       57 if ($list_prefixes) {
144 16         64 $_set_or_add_result->($newpfx);
145 16 100       62 $_set_or_add_result->($newpfx, 'prefix_paths', "$dir/$entry/", 'always_add') if $return_path;
146 16 50       30 $_set_or_add_result->($newpfx, 'library_path', $incdir, 'always_add') if $return_library_path;
147             }
148 40 100       152 push @prefixes, $newpfx if $recurse;
149             }
150             }
151 62 50 33     822 next unless $list_pod && $use_pod_dir;
152 0         0 $dir = join("/", $dir, "pod");
153 0 0       0 opendir($dh, $dir) or next;
154 0         0 while(defined(my $entry = readdir($dh))) {
155 0 0       0 if($entry =~ $pod_rx) {
156 0         0 my $key = $prefix.$1;
157 0 0 0     0 next if $re_wildcard && $key !~ $re_wildcard;
158 0         0 $_set_or_add_result->($key);
159 0 0       0 $_set_or_add_result->($key, 'pod_path', "$dir/$entry") if $return_path;
160 0 0       0 $_set_or_add_result->($key, 'library_path', $incdir) if $return_library_path;
161             }
162             }
163             }
164             }
165              
166             # we cannot filter prefixes early with wildcard because we need to dig down
167             # first and that would've been prevented if we had a wildcard like *::Foo.
168 17 100 100     57 if ($list_prefixes && $re_wildcard) {
169 2         10 for my $k (keys %results) {
170 6 50       21 next unless $k =~ /::\z/;
171 6         19 (my $k_nocolon = $k) =~ s/::\z//;
172 6 100 100     57 delete $results{$k} unless $k =~ $re_wildcard || $k_nocolon =~ $re_wildcard;
173             }
174             }
175              
176 17         156 return \%results;
177             }
178              
179             1;
180             # ABSTRACT: Module::List, with more options
181              
182             __END__