File Coverage

blib/lib/App/url.pm
Criterion Covered Total %
statement 20 29 68.9
branch n/a
condition n/a
subroutine 7 8 87.5
pod 1 1 100.0
total 28 38 73.6


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2 3     3   3261 use v5.26;
  3         12  
3 3     3   1227 use utf8;
  3         32  
  3         16  
4              
5             package App::url;
6              
7             our $VERSION = '1.007';
8              
9 3     3   162 use Carp qw(carp);
  3         6  
  3         176  
10 3     3   1012 use Mojo::Base -strict, -signatures;
  3         392634  
  3         26  
11 3     3   9862 use Mojo::URL;
  3         22731  
  3         25  
12 3     3   1667 use String::Sprintf;
  3         1594  
  3         147  
13              
14             =encoding utf8
15              
16             =head1 NAME
17              
18             App::url - format a URL according to a sprintf-like template
19              
20             =head1 SYNOPSIS
21              
22             $ url '%h' http://www.example.com/a/b/c
23             www.example.com
24              
25             $ url '%H' http://www.example.com/a/b/c
26             www
27              
28             $ url '%P' http://www.example.com/a/b/c
29             /a/b/c
30              
31             =head1 DESCRIPTION
32              
33             Decompose the URL and reformat it according to
34              
35             =head2 The formats
36              
37             =over 4
38              
39             =item * C<%a> - the path
40              
41             =item * C<%f> - the fragment
42              
43             =item * C<%h> - the hostname, with domain info
44              
45             =item * C<%H> - the hostname without domain info
46              
47             =item * C<%i> - the hostname in punycode
48              
49             =item * C<%I> - space-separated list of IP addresses for the host
50              
51             =item * C<%P> - the password of the userinfo portion
52              
53             =item * C<%p> - the port
54              
55             =item * C<%q> - the query string
56              
57             =item * C<%s> - the scheme
58              
59             =item * C<%S> - the public suffix
60              
61             =item * C<%u> - the complete URL
62              
63             =item * C<%U> - the username of the userinfo portion
64              
65             =back
66              
67             There are also some bonus formats unrelated to the URL:
68              
69             =over 4
70              
71             =item * C<%n> - newline
72              
73             =item * C<%t> - tab
74              
75             =item * C<%%> - literal percent
76              
77             =back
78              
79             =head2 Methods
80              
81             =over 4
82              
83             =item * run( TEMPLATE, ARRAY )
84              
85             Format each URL in ARRAY according to TEMPLATE and return an array
86             reference
87              
88             =back
89              
90             =head1 COPYRIGHT
91              
92             Copyright © 2020-2021, brian d foy, all rights reserved.
93              
94             =head1 LICENSE
95              
96             You can use this code under the terms of the Artistic License 2.
97              
98             =cut
99              
100 3     3   20 no warnings 'uninitialized';
  3         6  
  3         3449  
101              
102             # $w - width of field
103             # $v - value that corresponds to position in template
104             # $V - list of all values
105             # $l - letter
106             my $formatter = String::Sprintf->formatter(
107             a => sub ( $w, $v, $V, $l ) { $V->[0]->path },
108             f => sub ( $w, $v, $V, $l ) { $V->[0]->fragment },
109             h => sub ( $w, $v, $V, $l ) { $V->[0]->host },
110             H => sub ( $w, $v, $V, $l ) { ( split /\./, $V->[0]->host )[0] },
111             i => sub ( $w, $v, $V, $l ) { $V->[0]->ihost },
112             I => sub ( $w, $v, $V, $l ) {
113             state $rc = require Socket;
114             my @addresses = gethostbyname( $V->[0]->host );
115             @addresses = map { Socket::inet_ntoa($_) } @addresses[4..$#addresses];
116             "@addresses";
117             },
118             p => sub ( $w, $v, $V, $l ) { $V->[0]->port // do {
119             if( $V->[0]->protocol eq 'http' ) { 80 }
120             elsif( $V->[0]->protocol eq 'https' ) { 443 }
121             };
122             },
123             P => sub ( $w, $v, $V, $l ) { $V->[0]->password },
124             'q' => sub ( $w, $v, $V, $l ) { $V->[0]->query },
125             's' => sub ( $w, $v, $V, $l ) { $V->[0]->protocol },
126              
127             S => sub ( $w, $v, $V, $l ) {
128             state $rc = eval { require Net::PublicSuffixList };
129             unless( $rc ) {
130             carp "%${l} requires Net::PublicSuffixList\n";
131             return;
132             }
133             state $psl = Net::PublicSuffixList->new;
134             my $hash = $psl->split_host( $V->[0]->host );
135             $hash->{suffix};
136             },
137              
138             U => sub ( $w, $v, $V, $l ) { $V->[0]->username },
139             u => sub ( $w, $v, $V, $l ) { $V->[0]->to_string },
140              
141             n => sub { "\n" },
142             t => sub { "\t" },
143             '%' => sub { '%' },
144              
145             '*' => sub ( $w, $v, $V, $l ) { warn "Invalid specifier <$l>\n" },
146             );
147              
148 0     0 1   sub run ( $class, $template, @urls ) {
  0            
  0            
  0            
  0            
149 0           my @strings;
150              
151 0           foreach my $url ( @urls ) {
152 0           push @strings, $formatter->sprintf( $template, Mojo::URL->new($url) );
153             }
154              
155 0           return \@strings;
156             }