File Coverage

blib/lib/OPM/Parser.pm
Criterion Covered Total %
statement 136 138 98.5
branch 32 34 94.1
condition 11 13 84.6
subroutine 19 20 95.0
pod 4 4 100.0
total 202 209 96.6


line stmt bran cond sub pod time code
1             package OPM::Parser;
2              
3 9     9   678323 use v5.24;
  9         85  
4              
5             # ABSTRACT: Parser for the .opm file
6              
7 9     9   33 use strict;
  9         12  
  9         139  
8 9     9   29 use warnings;
  9         13  
  9         323  
9              
10             our $VERSION = '1.06'; # VERSION
11              
12 9     9   3526 use Moo;
  9         74877  
  9         38  
13 9     9   12619 use MooX::HandlesVia;
  9         63709  
  9         46  
14 9     9   3914 use OPM::Parser::Types qw(:all);
  9         26  
  9         78  
15              
16 9     9   362493 use MIME::Base64 ();
  9         4978  
  9         195  
17 9     9   3210 use OPM::Validate;
  9         26989  
  9         293  
18 9     9   2778 use Path::Class;
  9         249081  
  9         446  
19 9     9   3678 use Try::Tiny;
  9         8598  
  9         358  
20 9     9   4326 use XML::LibXML;
  9         313312  
  9         50  
21              
22             # declare attributes
23             has product => ( is => 'rw', isa => Str, );
24             has name => ( is => 'rw', isa => Str, );
25             has version => ( is => 'rw', isa => VersionString, );
26             has vendor => ( is => 'rw', isa => Str, );
27             has url => ( is => 'rw', isa => Str, );
28             has license => ( is => 'rw', isa => Str, );
29             has description => ( is => 'rw', isa => Str, );
30             has error_string => ( is => 'rw', isa => Str, );
31             has tree => ( is => 'rw', isa => XMLTree, );
32              
33             has opm_file => (
34             is => 'ro',
35             isa => Str,
36             required => 1,
37             );
38              
39             has files => (
40             is => 'rw',
41             isa => ArrayRef[HashRef],
42             default => sub{ [] },
43             handles_via => 'Array',
44             handles => {
45             add_file => 'push',
46             },
47             );
48              
49             has framework => (
50             handles_via => 'Array',
51             is => 'rw',
52             isa => ArrayRef[FrameworkVersionString],
53             default => sub { [] },
54             handles => {
55             add_framework => 'push',
56             },
57             );
58              
59             has framework_details => (
60             handles_via => 'Array',
61             is => 'rw',
62             isa => ArrayRef[HashRef],
63             default => sub { [] },
64             handles => {
65             add_framework_detail => 'push',
66             },
67             );
68              
69             has dependencies => (
70             handles_via => 'Array',
71             is => 'rw',
72             isa => ArrayRef[HashRef[Str]],
73             default => sub { [] },
74             handles => {
75             add_dependency => 'push',
76             },
77             );
78              
79              
80             sub documentation {
81 9     9 1 8566 my ($self,%params) = @_;
82            
83 9         12 my $doc_file;
84             my $found_file;
85            
86 9   100     25 my $lang = $params{lang} || 'en';
87 9   100     20 my $type = $params{type} || '';
88              
89 9         150 for my $file ( $self->files->@* ) {
90              
91 35         72 my $filename = $file->{filename};
92 35 100       89 next if $filename !~ m{ \A doc/ }x;
93            
94 19 100       30 if ( !$doc_file ) {
95 8         9 $doc_file = $file;
96 8         10 $found_file = $filename;
97             }
98            
99 19 100       105 next if $filename !~ m{ \A doc/$lang/ }x;
100            
101 9 100       41 if ( $found_file !~ m{ \A doc/$lang/ }x ) {
102 3         3 $doc_file = $file;
103 3         4 $found_file = $filename;
104             }
105            
106 9 100 100     89 next if $type && $filename !~ m{ \A doc/[^/]+/.*\.$type \z }x;
107            
108 7 100 100     39 if ( $type && $found_file !~ m{ \A doc/[^/]+/.*\.$type \z }x ) {
109 1         3 $doc_file = $file;
110 1         1 $found_file = $filename;
111             }
112              
113 7 100       73 last if $found_file =~ m{ \A doc/$lang/.*\.$type \z }x;
114             }
115            
116 9         27 return $doc_file;
117             }
118              
119             sub validate {
120 15     15 1 2985 my ($self) = @_;
121              
122 15         227 $self->error_string( '' );
123            
124 15 100       576 if ( !-e $self->opm_file ) {
125 1         20 $self->error_string( 'File does not exist' );
126 1         20 return;
127             }
128              
129             try {
130 14     14   578 my $fh = IO::File->new( $self->opm_file, 'r' );
131 14         1609 my $content = join '', $fh->getlines;
132 14         2434 OPM::Validate->validate( $content );
133             }
134             catch {
135 3     3   674 $self->error_string( 'opm file is invalid: ' . $_ );
136 14         129 };
137              
138 14 100       14902 return if $self->error_string;
139 11         86 return 1;
140             }
141              
142             sub parse {
143 10     10 1 5565 my ($self, %params) = @_;
144              
145 10         208 $self->error_string( '' );
146            
147 10 100       458 if ( !-e $self->opm_file ) {
148 1         19 $self->error_string( 'File does not exist' );
149 1         19 return;
150             }
151            
152 9         25 my $tree;
153             try {
154 9     9   924 my $parser = XML::LibXML->new;
155 9         171 $tree = $parser->parse_file( $self->opm_file );
156              
157 8         3018 $self->tree( $tree );
158             }
159             catch {
160 1     1   769 $self->error_string( 'Could not parse .opm: ' . $_ );
161 1         104 return;
162 9         84 };
163              
164 9 100       544 return if $self->error_string;
165            
166 8         65 my $is_valid = $self->validate;
167 8 50 33     52 if ( !$params{ignore_validation} && !$is_valid ) {
168 0         0 return;
169             }
170            
171 8         62 my $root = $tree->getDocumentElement;
172            
173             # collect basic data
174 8         54 $self->vendor( $root->findvalue( 'Vendor' ) );
175 8         1663 $self->name( $root->findvalue( 'Name' ) );
176 8         763 $self->license( $root->findvalue( 'License' ) );
177 8         734 $self->version( $root->findvalue( 'Version' ) );
178 8         175 $self->url( $root->findvalue( 'URL' ) );
179              
180 8         785 my $root_name = $root->nodeName;
181 8         37 $root_name =~ s{_package}{};
182              
183 8         118 $self->product( $root_name );
184            
185             # retrieve framework information
186 8         222 my @frameworks = $root->findnodes( 'Framework' );
187            
188             FILE:
189 8         207 for my $framework ( @frameworks ) {
190 34         968 my $framework_version = $framework->textContent;
191            
192 34         78 my %details = ( Content => $framework_version );
193 34         67 my $maximum = $framework->findvalue( '@Maximum' );
194 34         1299 my $minimum = $framework->findvalue( '@Minimum' );
195              
196 34 100       1108 $details{Maximum} = $maximum if $maximum;
197 34 100       57 $details{Minimum} = $minimum if $minimum;
198            
199             # push framework info to attribute
200 34         518 $self->add_framework( $framework_version );
201 34         1637 $self->add_framework_detail( \%details );
202             }
203              
204             # retrieve file information
205 8         265 my @files = $root->findnodes( 'Filelist/File' );
206            
207             FILE:
208 8         195 for my $file ( @files ) {
209 30         827 my $name = $file->findvalue( '@Location' );
210            
211             #next FILE if $name !~ m{ \. (?:pl|pm|pod|t) \z }xms;
212 30         1317 my $encode = $file->findvalue( '@Encode' );
213 30 100       1123 next FILE if $encode ne 'Base64';
214            
215 28         193 my $content_base64 = $file->textContent;
216 28         187 my $content = MIME::Base64::decode( $content_base64 );
217            
218             # push file info to attribute
219 28         460 $self->add_file({
220             filename => $name,
221             content => $content,
222             });
223             }
224            
225             # get description - english if available, any other language otherwise
226 8         218 my @descriptions = $root->findnodes( 'Description' );
227 8         167 my $description_string;
228            
229             DESCRIPTION:
230 8         18 for my $description ( @descriptions ) {
231 10         37 $description_string = $description->textContent;
232 10         25 my $language = $description->findvalue( '@Lang' );
233            
234 10 100       405 last DESCRIPTION if $language eq 'en';
235             }
236            
237 8         140 $self->description( $description_string );
238            
239             # get Addon and CPAN dependencies
240 8         214 my @addon_deps = $root->findnodes( 'PackageRequired' );
241 8         175 my @cpan_deps = $root->findnodes( 'ModuleRequired' );
242            
243 8         144 my %types = (
244             PackageRequired => 'Addon',
245             ModuleRequired => 'CPAN',
246             );
247            
248 8         18 for my $dep ( @addon_deps, @cpan_deps ) {
249 24         626 my $node_type = $dep->nodeName;
250 24         49 my $version = $dep->findvalue( '@Version' );
251 24         1031 my $dep_name = $dep->textContent;
252 24         48 my $dep_type = $types{$node_type};
253            
254 24         393 $self->add_dependency({
255             type => $dep_type,
256             version => $version,
257             name => $dep_name,
258             });
259             }
260            
261 8         286 return 1;
262             }
263              
264             sub as_sopm {
265 2     2 1 2228 my ($self) = @_;
266              
267 2         39 my $tree = $self->tree->cloneNode(1);
268 2         93 my $root = $tree->getDocumentElement;
269            
270 2         8 my @build_host = $root->findnodes( 'BuildHost' );
271 2         81 my @build_date = $root->findnodes( 'BuildDate' );
272            
273 2         57 $root->removeChild( $_ ) for @build_host;
274 2         34 $root->removeChild( $_ ) for @build_date;
275              
276             #$build_host->unbindNode() if $build_host;
277             #$build_date->unbindNode() if $build_date;
278            
279 2         19 my @files = $root->findnodes( 'Filelist/File' );
280 2         59 for my $file ( @files ) {
281 9         17 my ($encode) = $file->findnodes( '@Encode' );
282 9 50       220 $encode->unbindNode() if $encode;
283            
284 9         74 $file->removeChildNodes();
285             }
286            
287 2         8 return $tree->toString;
288             }
289              
290              
291             sub _get_xsd {
292              
293 0     0     return q~
294            
295            
296            
297            
298            
299              
300            
301            
302            
303            
304            
305            
306            
307            
308            
309            
310            
311            
312            
313            
314            
315            
316            
317            
318            
319            
320            
321            
322            
323            
324            
325            
326            
327            
328            
329            
330            
331            
332            
333            
334            
335            
336            
337            
338            
339            
340            
341            
342            
343            
344            
345            
346            
347            
348            
349            
350            
351            
352            
353            
354            
355            
356            
357            
358            
359            
360              
361            
362            
363            
364            
365            
366            
367            
368            
369            
370            
371            
372            
373            
374            
375            
376            
377            
378            
379            
380            
381            
382            
383            
384            
385            
386            
387            
388            
389            
390            
391            
392            
393            
394            
395            
396            
397            
398            
399            
400            
401            
402            
403            
404            
405            
406            
407            
408            
409            
410            
411            
412            
413            
414            
415            
416            
417            
418            
419            
420            
421            
422            
423            
424            
425            
426            
427            
428            
429            
430            
431            
432            
433            
434            
435            
436            
437            
438            
439            
440            
441            
442            
443            
444            
445            
446            
447            
448            
449            
450            
451            
452            
453            
454            
455            
456            
457            
458            
459            
460            
461            
462            
463            
464            
465            
466            
467            
468            
469            
470            
471            
472            
473            
474            
475            
476            
477            
478            
479            
480            
481            
482            
483            
484            
485            
486            
487            
488            
489            
490            
491            
492            
493            
494            
495            
496            
497            
498            
499            
500            
501            
502            
503            
504            
505            
506            
507            
508            
509            
510            
511            
512            
513            
514            
515            
516              
517            
518            
519            
520            
521            
522            
523            
524            
525            
526            
527            
528            
529            
530            
531            
532            
533            
534            
535            
536            
537            
538            
539            
540            
541            
542            
543            
544            
545            
546            
547            
548            
549            
550            
551            
552            
553            
554            
555            
556            
557            
558            
559            
560            
561            
562            
563            
564            
565            
566            
567            
568            
569            
570            
571            
572            
573            
574            
575            
576            
577            
578            
579            
580            
581            
582            
583            
584            
585            
586            
587            
588            
589            
590            
591            
592            
593            
594            
595            
596            
597              
598            
599            
600            
601            
602            
603            
604              
605            
606            
607            
608            
609            
610            
611              
612            
613            
614            
615            
616            
617            
618            
619            
620            
621            
622            
623            
624            
625            
626            
627            
628            
629              
630            
631            
632            
633            
634            
635            
636            
637              
638            
639            
640            
641            
642            
643            
644            
645              
646            
647            
648            
649            
650            
651            
652            
653            
654            
655            
656            
657            
658            
659            
660            
661            
662              
663            
664            
665            
666            
667            
668            
669            
670              
671            
672            
673            
674            
675            
676            
677            
678              
679            
680            
681            
682            
683            
684            
685            
686            
687            
688            
689            
690            
691            
692            
693            
694            
695            
696            
697              
698            
699            
700            
701            
702            
703            
704            
705            
706            
707             ~;
708             }
709              
710             1;
711              
712             __END__