The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Puncheur::Plugin::HandleStatic;
use 5.010;
use strict;
use warnings;

use MIME::Base64;
use Encode;

our @EXPORT = qw/to_psgi/;

sub to_psgi {
    my ($self, %opts) = @_;
    $self = $self->new unless ref $self;

    my $app = $self->Puncheur::to_psgi;
    if (delete $opts{handle_static} || $self->{handle_static}) {
        my $vpath = sub {
            for my $dir (@{ $self->template_dir }) {
                return $dir if ref $dir eq 'HASH';
            }
        }->();
        require Plack::App::File;
        my $orig_app = $app;
        my $app_file_1;
        my $app_file_2;

        my $asset_dir = $self->asset_dir;
        my $static_dir = File::Spec->catdir( $asset_dir, 'static' );
        $app = sub {
            my $env = shift;
            my $path_info = $env->{PATH_INFO};
            if ($vpath and my $content = $vpath->{$path_info} and $path_info =~ m{^/}) {
                my $ct = Plack::MIME->mime_type($path_info);

                my $app_name = $self->app_name;
                my $is_text = qr/\b(?:text|xml|javascript|json)\b/;
                state $cache = {};
                if ($cache->{$app_name}{$path_info}) {
                    $content = $cache->{$app_name}{$path_info};
                }
                else {
                    if ($ct =~ $is_text) {
                        $content = encode($self->encoding, $content);
                    }
                    else {
                        $content = decode_base64($content);
                    }
                    $cache->{$app_name}{$path_info} = $content;
                }

                if ($ct =~ $is_text) {
                    my $encoding = $self->encoding;
                    $encoding = lc $encoding->mime_name if ref $encoding;
                    $ct .= "; charset=$encoding";
                }
                return [200, ['Content-Type' => $ct, 'Content-Length' => length($content)], [$content]];
            }
            elsif ($path_info =~ qr{^(?:/robots\.txt|/favicon\.ico)$}) {
                $app_file_1 ||= Plack::App::File->new({ root => $static_dir });
                return $app_file_1->call($env);
            }
            elsif ($path_info =~ m{^/static/}) {
                $app_file_2 ||= Plack::App::File->new({ root => $asset_dir });
                return $app_file_2->call($env);
            }
            else {
                return $orig_app->($env);
            }
        };
    }
    $app;
}

1;