File Coverage

blib/lib/App/Web/VPKBuilder.pm
Criterion Covered Total %
statement 59 136 43.3
branch 0 24 0.0
condition 0 4 0.0
subroutine 20 31 64.5
pod 2 6 33.3
total 81 201 40.3


line stmt bran cond sub pod time code
1             package App::Web::VPKBuilder;
2              
3 1     1   21386 use 5.014000;
  1         4  
4 1     1   6 use strict;
  1         1  
  1         22  
5 1     1   4 use warnings;
  1         12  
  1         37  
6 1     1   726 use parent qw/Plack::Component/;
  1         301  
  1         6  
7 1     1   15512 use re '/s';
  1         2  
  1         96  
8             our $VERSION = '0.001';
9              
10 1     1   6 use File::Find qw/find/;
  1         2  
  1         75  
11 1     1   12 use File::Path qw/remove_tree/;
  1         2  
  1         64  
12 1     1   787 use File::Spec::Functions qw/abs2rel catfile rel2abs/;
  1         935  
  1         91  
13 1     1   1129 use File::Temp qw/tempdir/;
  1         13953  
  1         67  
14 1     1   6520 use IO::Compress::Zip qw/zip ZIP_CM_LZMA/;
  1         87852  
  1         103  
15 1     1   903 use sigtrap qw/die normal-signals/;
  1         1329  
  1         9  
16              
17 1     1   887 use Data::Diver qw/DiveRef/;
  1         950  
  1         74  
18 1     1   847 use File::Slurp qw/write_file/;
  1         13638  
  1         67  
19 1     1   1484 use HTML::Element;
  1         25716  
  1         5  
20 1     1   1286 use HTML::TreeBuilder;
  1         8392  
  1         9  
21 1     1   866 use Hash::Merge qw/merge/;
  1         2926  
  1         78  
22 1     1   850 use List::MoreUtils qw/uniq/;
  1         12433  
  1         9  
23 1     1   1453 use Plack::Request;
  1         52392  
  1         39  
24 1     1   776 use Sort::ByExample qw/sbe/;
  1         13418  
  1         7  
25 1     1   961 use YAML qw/LoadFile/;
  1         7471  
  1         1555  
26              
27             sub new {
28 0     0 1   my $self = shift->SUPER::new(@_);
29 0           $self->{cfg} = {};
30 0           for (sort ) {
31 0           my $cfg = LoadFile $_;
32 0           $self->{cfg} = merge $self->{cfg}, $cfg
33             }
34 0   0       $self->{cfg}{vpk_extension} //= 'vpk';
35 0     0     $self->{cfg}{sort} = sbe $self->{cfg}{sort_order}, { fallback => sub { shift cmp shift } };
  0            
36 0           $self
37             }
38              
39             sub addpkg {
40 0     0 0   my ($pkg, $dir) = @_;
41 0 0         return unless $pkg =~ /^[a-zA-Z0-9_-]+$/aa;
42 0           my @dirs = ($dir);
43             find {
44 0     0     postprocess => sub { pop @dirs },
45             wanted => sub {
46 0     0     my $dest = catfile @dirs, $_;
47 0 0         mkdir $dest if -d;
48 0 0         push @dirs, $_ if -d;
49 0 0         link $_, $dest if -f;
50 0           }}, catfile 'pkg', $pkg;
51             }
52              
53             sub makepkg {
54 0     0 0   my ($self, @pkgs) = @_;
55 0           mkdir $self->{cfg}{dir};
56 0           my $dir = rel2abs tempdir 'workXXXX', DIR => $self->{cfg}{dir};
57 0           my $dest = catfile $dir, 'pkg';
58 0           mkdir $dest;
59 0           @pkgs = grep { exists $self->{cfg}{pkgs}{$_} } @pkgs;
  0            
60 0   0       push @pkgs, split /,/, ($self->{cfg}{pkgs}{$_}{deps} // '') for @pkgs;
61 0           @pkgs = uniq @pkgs;
62 0           addpkg $_, $dest for @pkgs;
63 0           write_file catfile ($dir, 'readme.txt'), $self->{cfg}{readme};
64 0           my @zip_files = catfile $dir, 'readme.txt';
65 0 0         if ($self->{cfg}{vpk}) {
66 0           system $self->{cfg}{vpk} => $dest;
67 0           push @zip_files, catfile $dir, "pkg.$self->{cfg}{vpk_extension}"
68             } else {
69 0 0   0     find sub { push @zip_files, $File::Find::name if -f }, $dest;
  0            
70             }
71 0     0     zip \@zip_files, catfile($dir, 'pkg.zip'), FilterName => sub { $_ = abs2rel $_, $dir }, -Level => 1;
  0            
72 0 0         open my $fh, '<', catfile $dir, 'pkg.zip' or return [500, ['Content-Type' => 'text/plain;charset=utf-8'], ['Error opening pkg.zip']]; ## no critic (RequireBriefOpen)
73 0           remove_tree $dir;
74 0           [200, ['Content-Type' => 'application/zip', 'Content-Disposition' => 'attachment; filename=pkg.zip'], $fh]
75             }
76              
77             sub makelist {
78 0     0 0   my ($self, $elem, $tree, $lvl, $key) = @_;
79 0           my $name = HTML::Element->new('span', class => 'name');
80 0           $name->push_content($key);
81 0 0         $elem->push_content($name) if defined $key;
82 0 0         if (ref $tree eq 'ARRAY') {
83 0           my $sel = HTML::Element->new('select', name => 'pkg');
84 0           my $opt = HTML::Element->new('option', value => '');
85 0           $opt->push_content('None');
86 0           $sel->push_content($opt);
87 0           for my $pkg (sort { $a->{name} cmp $b->{name} } @$tree) {
  0            
88 0 0         my $option = HTML::Element->new('option', value => $pkg->{pkg}, $pkg->{default} ? (selected => 'selected') : ());
89 0           $option->push_content($pkg->{name});
90 0           $sel->push_content($option);
91             }
92 0           $elem->push_content($sel);
93             } else {
94 0           my $ul = HTML::Element->new('ul');
95 0           for my $key ($self->{cfg}{sort}->(keys %$tree)) {
96 0           my $li = HTML::Element->new('li', class => "level$lvl");
97 0           $self->makelist($li, $tree->{$key}, $lvl + 1, $key);
98 0           $ul->push_content($li);
99             }
100 0           $elem->push_content($ul);
101             }
102             }
103              
104             sub makeindex {
105 0     0 0   my ($self) = @_;
106 0           my ($pkgs, $tree) = ($self->{cfg}{pkgs}, {});
107 0           for (keys %$pkgs) {
108 0           my $ref = DiveRef ($tree, split /,/, $pkgs->{$_}{path});
109 0 0         $$ref = [] unless ref $$ref eq 'ARRAY';
110 0           push @{$$ref}, {pkg => $_, name => $pkgs->{$_}{name}, default => $pkgs->{$_}{default}};
  0            
111             }
112 0           my $html = HTML::TreeBuilder->new_from_file('index.html');
113 0           $self->makelist(scalar $html->look_down(id => 'list'), $tree, 1);
114 0           my $ret = $html->as_HTML('', ' ');
115 0           utf8::encode($ret);
116 0           [200, ['Content-Type' => 'text/html;charset=utf-8'], [$ret]]
117             }
118              
119             sub call{
120 0     0 1   my ($self, $env) = @_;
121 0           my $req = Plack::Request->new($env);
122 0 0         return $self->makepkg($req->param('pkg')) if $req->path eq '/makepkg';
123 0           $self->makeindex;
124             }
125              
126             1;
127             __END__