File Coverage

blib/lib/App/lms.pm
Criterion Covered Total %
statement 44 137 32.1
branch 0 52 0.0
condition 0 15 0.0
subroutine 15 22 68.1
pod 0 2 0.0
total 59 228 25.8


line stmt bran cond sub pod time code
1             package App::lms;
2              
3             our $VERSION = "0.99";
4              
5 1     1   285945 use v5.14;
  1         4  
6 1     1   3 use warnings;
  1         1  
  1         74  
7              
8 1     1   557 use utf8;
  1         275  
  1         5  
9 1     1   28 use Encode;
  1         2  
  1         114  
10 1     1   740 use open IO => 'utf8', ':std';
  1         1393  
  1         9  
11 1     1   628 use Pod::Usage;
  1         62153  
  1         205  
12 1     1   10 use List::Util qw(any first);
  1         2  
  1         71  
13 1     1   585 use App::lms::Util;
  1         3  
  1         35  
14 1     1   411 use Text::ParseWords qw(shellwords);
  1         1395  
  1         67  
15              
16 1     1   621 use Getopt::EX::Hashed; {
  1         15799  
  1         23  
17             Getopt::EX::Hashed->configure(DEFAULT => [ is => 'rw' ]);
18             has one => ' 1 ' ;
19             has debug => ' d + ' ;
20             has dryrun => ' n ' ;
21             has raw => ' r ' ;
22             has help => ' h ' , action => sub {
23             pod2usage(-verbose => 99, -sections => [qw(SYNOPSIS)])
24             } ;
25             has list => ' l + ' ;
26             has man => ' m ' ;
27             has number => ' N ! ' , default => 0 ;
28             has version => ' v ' , action => sub { say "Version: $VERSION"; exit } ;
29             has pager => ' p =s ' ;
30             has suffix => ' =s ' , default => [ qw( .pm ) ] ;
31             has type => ' t =s ' , default => 'Command:Perl:Python:Ruby:Node' ;
32             has py => ' ' , action => sub { $_->type('Python') } ;
33             has pl => ' ' , action => sub { $_->type('Perl') } ;
34             has rb => ' ' , action => sub { $_->type('Ruby') } ;
35             has nd => ' ' , action => sub { $_->type('Node') } ;
36             has bat_theme => ' % ' ,
37             default => { light => 'Coldark-Cold', dark => 'Coldark-Dark' } ;
38             has skip => ' =s@ ' ,
39             default => [ $ENV{OPTEX_BINDIR} || ".optex.d/bin" ] ;
40 1     1   460 } no Getopt::EX::Hashed;
  1         1  
  1         4  
41              
42             sub run {
43 0     0 0   my $app = shift;
44 0 0         @_ = map { utf8::is_utf8($_) ? $_ : decode('utf8', $_) } @_;
  0            
45 0           local @ARGV = splice @_;
46              
47 1     1   752 use Getopt::EX::Long qw(GetOptions Configure ExConfigure);
  1         95795  
  1         293  
48 0           ExConfigure BASECLASS => [ __PACKAGE__, "Getopt::EX" ];
49 0           Configure qw(bundling no_getopt_compat);
50 0 0         $app->getopt || pod2usage();
51              
52 0           my $name = pop @ARGV;
53 0 0         if (!defined $name) {
54 0 0         if ($app->man) {
55 0   0       my $script = $ENV{LMS_SCRIPT_PATH} // $0;
56 0           exec 'perldoc', $script;
57 0           die "perldoc: $!\n";
58             }
59 0           pod2usage();
60             }
61 0           my @option = splice @ARGV;
62 0   0       my $pager = $app->pager || $ENV{'LMS_PAGER'} || _default_pager($app);
63              
64 0           my @found;
65             my $found_type;
66 0           for my $type (split /:+/, $app->type) {
67 0           $type = _normalize_type($type);
68 0           my $handler = __PACKAGE__ . '::' . $type;
69 0 0         warn "Trying handler: $type\n" if $app->debug;
70 1     1   9 no strict 'refs';
  1         1  
  1         284  
71 0 0         if (eval "require $handler") {
72 0           my @paths = grep { defined } &{"$handler\::get_path"}($app, $name);
  0            
  0            
73 0 0         if (@paths) {
74 0 0         warn "Found by $type: @paths\n" if $app->debug;
75 0           push @found, @paths;
76 0   0       $found_type //= $type;
77 0 0         last if $app->one;
78             } else {
79 0 0         warn "Not found by $type\n" if $app->debug;
80             }
81             } else {
82 0           warn $@;
83             }
84             }
85              
86 0 0         if (not @found) {
87 0           warn "$name: Nothing found.\n";
88 0           return 1;
89             }
90              
91 0 0         if (my $level = $app->list) {
92 0 0         if ($level > 1) {
93 0           system 'ls', '-l', @found;
94             } else {
95 0           say for @found;
96             }
97 0           return 0;
98             }
99              
100 0 0         if ($app->man) {
101 0           my $handler = __PACKAGE__ . '::' . $found_type;
102 1     1   6 no strict 'refs';
  1         1  
  1         402  
103 0           my @cmd = &{"$handler\::man_cmd"}($app, $name, $found[0]);
  0            
104 0 0         if ($app->dryrun) {
105 0           say "@cmd";
106 0           return 0;
107             }
108 0           exec @cmd;
109 0           die "$found_type man: $!\n";
110             }
111              
112             @found = grep {
113 0 0         not &is_binary($_) or do {
  0 0          
114 0           system 'file', $_;
115 0           0;
116             }
117             } @found or return 0;
118              
119 0           my @pager_opts;
120 0 0         if (defined $app->number) {
121 0           my $pager_name = (shellwords($pager))[0];
122 0           $pager_name =~ s{.*/}{}; # basename
123 0 0         if ($pager_name eq 'bat') {
    0          
124 0 0         push @pager_opts, $app->number ? '--style=full' : '--style=header,grid,snip';
125             } elsif ($pager_name eq 'less') {
126 0 0         push @pager_opts, '-N' if $app->number;
127             }
128             }
129 0           my @cmd = (shellwords($pager), @pager_opts, @option, @found);
130 0 0         if ($app->dryrun) {
131 0           say "@cmd";
132 0           return 0;
133             }
134 0           exec @cmd;
135 0           die "$pager: $!\n";
136             }
137              
138 1     1   9 use List::Util qw(none);
  1         23  
  1         554  
139              
140             sub valid {
141 0     0 0   my $app = shift;
142 0           state $sub = do {
143 0           my @re = map { qr/\Q$_\E$/ } @{$app->skip};
  0            
  0            
144 0     0     sub { none { $_[0] =~ $_ } @re };
  0            
  0            
145             };
146 0           $sub->(@_);
147             }
148              
149             sub _default_pager {
150 0     0     my $app = shift;
151 0           state $pager = do {
152 0     0     my $bat = first { -x } map { "$_/bat" } split /:/, $ENV{PATH};
  0            
  0            
153 0 0         if ($bat) {
154 0   0       $ENV{BAT_THEME} //= _bat_theme($app->bat_theme);
155 0           "$bat --force-colorization";
156             } else {
157 0           'less';
158             }
159             };
160             }
161              
162             sub _bat_theme {
163 0     0     my $themes = shift;
164 0           my $lum = eval {
165 0           require Getopt::EX::termcolor;
166 0           Getopt::EX::termcolor::get_luminance();
167             };
168 0 0         return () unless defined $lum;
169 0 0         $themes->{$lum < 50 ? 'dark' : 'light'};
170             }
171              
172             sub _normalize_type {
173 0     0     my $type = shift;
174 0           state $map = {
175             command => 'Command',
176             perl => 'Perl',
177             python => 'Python',
178             ruby => 'Ruby',
179             node => 'Node',
180             };
181 0   0       $map->{lc $type} // ucfirst lc $type;
182             }
183              
184             1;
185              
186             __END__