File Coverage

blib/lib/App/lookup.pm
Criterion Covered Total %
statement 58 102 56.8
branch 4 40 10.0
condition 3 13 23.0
subroutine 11 16 68.7
pod 0 10 0.0
total 76 181 41.9


line stmt bran cond sub pod time code
1             package App::lookup;
2              
3 2     2   34084 use strict;
  2         3  
  2         68  
4 2     2   9 use warnings;
  2         3  
  2         48  
5              
6 2     2   9 use File::Spec;
  2         8  
  2         62  
7 2     2   3556 use Getopt::Long qw(:config bundling no_ignore_case);
  2         66877  
  2         14  
8 2     2   11785 use Text::Abbrev 'abbrev';
  2         109  
  2         154  
9 2     2   15592 use Text::Wrap 'wrap';
  2         11653  
  2         3297  
10              
11             our $VERSION = '0.06';
12              
13             sub parse_command_line {
14 0 0   0 0 0 GetOptions(\my %opts, 'help|h|?', 'man|m', 'version|v',
15             'sites|s', 'abbrevs|a', 'config-file|c=s', 'web-browser|w=s')
16             or print_usage(-verbose => 0, -exitval => 1);
17              
18 0 0       0 if ($opts{help}) {
    0          
    0          
19 0         0 print_usage(
20             -verbose => 99,
21             -sections => [qw(USAGE EXAMPLES OPTIONS)],
22             -exitval => 0,
23             );
24             }
25             elsif ($opts{man}) {
26 0         0 print_usage(-verbose => 2, -exitval => 0);
27             }
28             elsif ($opts{version}) {
29 0 0       0 print_version() and exit;
30             }
31              
32 0 0 0     0 die "The config file '$opts{'config-file'}' does not exist\n"
33             if $opts{'config-file'} and not -f $opts{'config-file'};
34              
35 0         0 $opts{sitename} = shift @ARGV;
36 0         0 $opts{query} = join ' ', @ARGV;
37              
38 0         0 return \%opts;
39             }
40              
41             # thin wrapper around Pod::Usage::pod2usage since Pod::Usage causes
42             # insignificant-but-noticable-enough-to-be-annoying delay in startup time.
43             sub print_usage {
44 0     0 0 0 require Pod::Usage;
45 0         0 Pod::Usage::pod2usage(@_);
46             }
47              
48             sub print_version {
49 0     0 0 0 require File::Basename;
50 0         0 my $progname = File::Basename::basename($0);
51 0         0 print "This is $progname version $VERSION running on perl $]\n";
52 0         0 return 1;
53             }
54              
55             sub read_config_file {
56 4     4 0 8 my $config_file = shift;
57              
58 4         1134 require Config::Tiny;
59 4 50       1092 Config::Tiny->read($config_file)
60             or die wrap(
61             '', '',
62             sprintf(
63             "Error when reading configuration file %s: %s\n",
64             $config_file, Config::Tiny->errstr
65             ));
66             }
67              
68             sub initialize_sites {
69 4   33 4 0 5936 my $config_file = shift || File::Spec->catfile($ENV{HOME}, '.lookuprc');
70 4         16 my %predefined_sites = (google => 'http://google.com/search?q=%(query)');
71              
72 4 50       115 return \%predefined_sites unless -f $config_file;
73              
74 4         13 my $config = read_config_file($config_file);
75 4   50     1029 my $aliases = $config->{alias} || {};
76 4   50     13 my $user_sites = $config->{sites} || {};
77              
78             # merge the hashes, user defined sites take precedence over the predefined
79             # ones (we only have one predefined site anyway)
80 4         24 my %sites = (%$user_sites, %predefined_sites);
81              
82 4         20 while (my ($alias, $original) = each %$aliases) {
83 4 50       10 if (exists $sites{$original}) {
84 4         9 $sites{$alias} = $sites{$original};
85 4         17 delete $sites{$original};
86             }
87             else {
88 0         0 warn "The alias '$alias' doesn't match any site\n";
89             }
90             }
91 4         41 return \%sites;
92             }
93              
94             sub find_max_length {
95 6     6 0 33 require List::Util;
96 6         14 List::Util::max(map { length $_ } @_);
  20         49  
97             }
98              
99             sub print_sites {
100 2     2 0 3 my $sites = shift;
101 2         9 my $maxlen = find_max_length(keys %$sites);
102              
103 2         14 for my $site (sort keys %$sites) {
104 8         192 printf "- %-${maxlen}s : %s\n", $site, $sites->{$site};
105             }
106              
107 2         13 return 1;
108             }
109              
110             sub print_abbrevs {
111 1     1 0 957 my ($abbrevs, $sites) = @_;
112 1         2 my $data;
113              
114 1         8 while (my ($abbr, $sitename) = each %$abbrevs) {
115 15 50       32 push @{ $data->{$sitename} }, $abbr if $sites->{$sitename};
  15         50  
116             }
117              
118 1         8 for my $sitename (sort keys %$data) {
119 4         5 my $site = "Name";
120 4         7 my $url = "URL";
121 4         4 my $abstr = "Abbrev(s)";
122 4         10 my $maxlen = find_max_length($site, $url, $abstr);
123              
124 4         83 printf "%-${maxlen}s : %s\n", $site, $sitename;
125 4         50 printf "%-${maxlen}s : %s\n", $url, $sites->{$sitename};
126              
127 4         21 my $joined_abbrevs = sprintf("%-${maxlen}s : %s",
128 4         10 $abstr, join ', ', sort @{ $data->{$sitename} });
129              
130             # + 3 to adjust the addition of colon and whitespace
131 4         37 print wrap('', ' ' x ($maxlen + 3), $joined_abbrevs);
132 4         893 print "\n\n";
133             }
134              
135 1         9 return 1;
136             }
137              
138             sub browse_url {
139 0     0 0   my ($url, $browser) = @_;
140              
141 0           require URI::Encode;
142 0           $url = URI::Encode::uri_encode($url);
143              
144 0 0         if ($browser) {
145 0           require IPC::System::Simple;
146 0           IPC::System::Simple::systemx(split(/\s/, $browser), $url);
147             }
148             else {
149 0           require Browser::Open;
150 0 0         if (defined(my $status = Browser::Open::open_browser($url))) {
151 0 0         if ($status != 0) {
152 0           die "Error when opening $url with web browser\n";
153             }
154             }
155             else {
156 0           die "Could not found the command to execute the web browser\n";
157             }
158             }
159              
160             }
161              
162             sub run {
163 0     0 0   my $opts = parse_command_line();
164 0           my $sites = initialize_sites($opts->{'config-file'});
165 0           my $abbrevs = abbrev(keys %$sites);
166 0           my $sitename = $opts->{sitename};
167              
168 0 0 0       if ($opts->{sites}) {
    0          
    0          
169 0 0         print_sites($sites) and exit;
170             }
171             elsif ($opts->{abbrevs}) {
172 0 0         print_abbrevs($abbrevs, $sites) and exit;
173             }
174             elsif (not $sitename or not $opts->{query}) {
175 0           print_usage('Too few arguments');
176             }
177              
178 0 0         if (my $valid_site = $abbrevs->{$sitename}) {
179 0           my $url = $sites->{$valid_site};
180              
181 0           $url =~ s{%\(query\)}{$opts->{query}};
182              
183 0           browse_url($url, $opts->{'web-browser'});
184             }
185             else {
186 0 0         if (my @ambiguous = grep { /^$sitename/ } sort keys %$abbrevs) {
  0            
187 0           print wrap(
188             '', '',
189             sprintf(
190             "Ambiguous site name: '$sitename'. Did you mean one of "
191             . "the following sites/abbreviations: %s?\n",
192             join(', ', @ambiguous)));
193             }
194             else {
195 0           print "Could not find sites that match '$sitename'\n";
196             }
197 0           exit 1;
198             }
199             }
200              
201             1;
202              
203             __END__