File Coverage

blib/lib/IO/Detect.pm
Criterion Covered Total %
statement 125 141 88.6
branch 35 56 62.5
condition 24 49 48.9
subroutine 36 38 94.7
pod 3 3 100.0
total 223 287 77.7


line stmt bran cond sub pod time code
1             package IO::Detect;
2              
3 27     9   380561 use 5.008;
  9         33  
  27         706  
4 13     9   65 use constant { false => !1, true => !0 };
  13         34  
  13         2486  
5 13     9   108 use strict;
  10         28  
  9         341  
6 9     9   51 use warnings;
  9         16  
  9         459  
7 13     9   10716 use if $] < 5.010, 'UNIVERSAL::DOES';
  13         94  
  13         80  
8              
9             BEGIN {
10 13     9   558 $IO::Detect::AUTHORITY = 'cpan:TOBYINK';
11 9         231 $IO::Detect::VERSION = '0.202';
12             }
13              
14 9     9   6532 use namespace::clean 0.19;
  9         187908  
  9         85  
15              
16 9         9648 EXPORTER:
17             {
18 9     9   2192 use base "Exporter::Tiny";
  9         22  
19            
20             our %_CONSTANTS;
21             our @EXPORT = qw( is_filehandle is_filename is_fileuri );
22             our @EXPORT_OK = (
23             qw( is_filehandle is_filename is_fileuri ),
24             qw( FileHandle FileName FileUri ),
25             qw( ducktype as_filehandle ),
26             );
27             our %EXPORT_TAGS = (
28             smartmatch => [qw( FileHandle FileName FileUri )],
29             );
30            
31             sub _exporter_validate_opts
32             {
33 7     7   612 require B;
34 7         16 my $class = shift;
35             $_[0]{exporter} ||= sub {
36 13     13   1524 my $into = $_[0]{into};
37 13         20 my ($name, $sym) = @{ $_[1] };
  13         30  
38 13         247 for (grep ref, $into->can($name))
39             {
40 0 50       0 B::svref_2object($_)->STASH->NAME eq $into
41             and _croak("Refusing to overwrite local sub '$name' with export from $class");
42             }
43 13         101 "namespace::clean"->import(-cleanee => $_[0]{into}, $name);
44 9     9   25702 no strict qw(refs);
  9         25  
  9         442  
45 9     9   83 no warnings qw(redefine prototype);
  9         97  
  9         1301  
46 13         481 *{"$into\::$name"} = $sym;
  13         112  
47             }
48 7   100     95 }
49             }
50              
51 9     9   11333 use overload qw<>;
  9         6663  
  9         232  
52 9     9   58 use Scalar::Util qw< blessed openhandle reftype >;
  9         16  
  9         1237  
53 9     9   64 use Carp qw;
  9         26  
  9         462  
54 9     9   155729 use URI::file;
  9         1017625  
  9         17794  
55              
56             sub _lu {
57 0     0   0 require lexical::underscore;
58 0         0 goto \&lexical::underscore;
59             }
60              
61             sub _ducktype
62             {
63 8     8   37 my ($object, $methods) = @_;
64 8 100       160 return unless blessed $object;
65            
66 2 100       4 foreach my $m (@{ $methods || [] })
  2         15  
67             {
68 2 100       31 return unless $object->can($m);
69             }
70            
71 1         11 return true;
72             }
73              
74             sub _generate_ducktype
75             {
76 1     1   2127 my ($class, $name, $arg) = @_;
77 1         3 my $methods = $arg->{methods};
78             return sub (;$) {
79 2 50   2   93 @_ = ${+_lu} unless @_;
  0         0  
80 2         7 push @_, $methods;
81 2         9 goto \&_ducktype;
82 1         11 };
83             }
84              
85             my $expected_methods = [
86             qw(close eof fcntl fileno getc getline getlines ioctl read print stat)
87             ];
88              
89             sub is_filehandle (;$)
90             {
91 5 100   23 1 21 my $fh = @_ ? shift : ${+_lu};
  0         0  
92            
93 5 100       35 return true if openhandle $fh;
94            
95             # Logic from IO::Handle::Util
96             {
97 2         5 my $reftype = reftype($fh);
  2         9  
98 2 100       11 $reftype = '' unless defined $reftype;
99            
100 2 50 66     28 if ($reftype eq 'IO'
  0   66     0  
101             or $reftype eq 'GLOB' && *{$fh}{IO})
102             {
103 0         0 for ($fh->fileno, fileno($fh))
104             {
105 0 50       0 return unless defined;
106 0 0       0 return unless $_ >= 0;
107             }
108            
109 0         0 return true;
110             }
111             }
112            
113 2 50 33     15 return true if blessed $fh && $fh->DOES('IO::Handle');
114 2 50 33     15 return true if blessed $fh && $fh->DOES('FileHandle');
115 2 50 33     14 return true if blessed $fh && $fh->DOES('IO::All');
116            
117 2         10 return _ducktype $fh, $expected_methods;
118             }
119              
120             sub _oneline ($)
121             {
122 1     1   11 !! ( $_[0] !~ /\r?\n|\r/s )
123             }
124              
125             sub is_filename (;$)
126             {
127 4 50   4 1 32 my $f = @_ ? shift : ${+_lu};
  0         0  
128 4 50 33     18 return true if blessed $f && $f->DOES('IO::All');
129 4 50 33     12 return true if blessed $f && $f->DOES('Path::Class::Entity');
130 4 50 0     13 return ( length "$f" and _oneline "$f" )
      33        
131             if blessed $f && overload::Method($f, q[""]);
132 4 100 66     28 return ( length $f and _oneline $f )
      100        
133             if defined $f && !ref $f;
134 2         11 return;
135             }
136              
137             sub is_fileuri (;$)
138             {
139 3 50   3 1 472 my $f = @_ ? shift : ${+_lu};
  0         0  
140 3 50 33     24 return $f if blessed $f && $f->DOES('URI::file');
141 3 50 33     15 return URI::file->new($f->uri) if blessed $f && $f->DOES('RDF::Trine::Node::Resource');
142 3 50       20 return URI::file->new($f) if $f =~ m{^file://\S+}i;
143 3         17 return;
144             }
145              
146             sub _generate_as_filehandle
147             {
148 10     10   338 my ($class, $name, $arg) = @_;
149 10   100     96 my $default_mode = $arg->{mode} || '<';
150            
151             return sub (;$$)
152             {
153 1 50   1   13 my $f = @_ ? shift : ${+_lu};
  0         0  
154 1 50       4 return $f if is_filehandle($f);
155            
156 1 50       5 if (my $uri = is_fileuri($f))
157 0         0 { $f = $uri->file }
158            
159 1   33     8 my $mode = shift || $default_mode;
160 1 50   1   39 open my $fh, $mode, $f
  1         11  
  1         1  
  1         10  
161             or croak "Cannot open '$f' with mode '$mode': $!, died";
162 1         41110 return $fh;
163 10         940 };
164             }
165              
166             *as_filehandle = __PACKAGE__->_generate_as_filehandle('as_filehandle', +{});
167              
168             {
169             package IO::Detect::SmartMatcher;
170             BEGIN {
171 9     9   30 $IO::Detect::SmartMatcher::AUTHORITY = 'cpan:TOBYINK';
172 9         232 $IO::Detect::SmartMatcher::VERSION = '0.202';
173             }
174 9     9   132 use Scalar::Util qw< blessed >;
  9         21  
  9         586  
175 9     9   58 use overload (); no warnings 'overload'; # '~~' unavailable in Perl 5.8
  9     9   21  
  9         221  
  9         49  
  9         21  
  9         633  
176             use overload
177 9         83 '""' => 'to_string',
178             '~~' => 'check',
179             '==' => 'check',
180             'eq' => 'check',
181 9     9   51 fallback => 1;
  9         16  
182             sub check
183             {
184 0     0   0 my ($self, $thing) = @_;
185 0         0 $self->[1]->($thing);
186             }
187             sub to_string
188             {
189 2     2   66 shift->[0]
190             }
191             sub new
192             {
193 29     29   1017608 my $proto = shift;
194 29 100 66     229 if (blessed $proto and $proto->isa(__PACKAGE__))
195             {
196 2         55 return "$proto"->new(@_);
197             }
198 27         2413 bless \@_ => $proto;
199             }
200             }
201              
202 9     9   3410 use constant FileHandle => IO::Detect::SmartMatcher::->new(FileHandle => \&is_filehandle);
  9         15  
  9         123  
203 9     9   55 use constant FileName => IO::Detect::SmartMatcher::->new(FileName => \&is_filename);
  9         13  
  9         42  
204 9     9   47 use constant FileUri => IO::Detect::SmartMatcher::->new(FileUri => \&is_fileuri);
  9         20  
  9         36  
205              
206             true;
207              
208             __END__