#!/usr/bin/env perl
use strict;
use warnings;
use File::Basename qw(basename dirname);
use File::Copy qw(copy);
use File::Slurp qw(read_file);
use File::Temp qw(tempfile);
use Getopt::Long qw(:config posix_default no_ignore_case bundling auto_help);
use Pod::Usage qw(pod2usage);

GetOptions(
    \my %opt, qw/
    lib|I=s
    filename|f=s
    verbose|v
    help|h
/) or pod2usage(1);

pod2usage(-exitval => 1, -verbose => 2) if $opt{help};
pod2usage(2) if grep { !exists $opt{$_} } qw/filename/;

STDIN->blocking(0);
my $script_text = do { local $/; <STDIN> };
my ($script_fh, $script) = tempfile();
if (defined $script_text) {
    $script_fh->print($script_text);
} else {
    copy $opt{filename} => $script_fh;
}
$script_fh->close;

chdir dirname $opt{filename};
chomp(my $git_root = `git rev-parse --show-toplevel`);
chdir $git_root if -d $git_root;

my ($runner_fh, $runner) = tempfile();

my ($shebang) = read_file($script);
my $bin;
($bin) = $shebang =~ /^#!(\S*)/ if defined $shebang;

# $bin is NOT `env`, use the binary itself
if (defined $bin && basename($bin) ne 'env') {
    $runner_fh->print(<<EOS);
export PERL5LIB=@{[$opt{lib} // '']}:\$PERL5LIB
$bin -c $script
EOS

# Otherwise, it should use perl resolved by $PATH and plenv.
} else {
    $runner_fh->print(<<EOS);
#!/bin/bash
if which plenv > /dev/null; then
  eval "\$(plenv init -)"
fi
if which direnv > /dev/null; then
  eval "\$(direnv export bash)"
fi
export PERL5LIB=@{[$opt{lib} // '']}:\$PERL5LIB
perl -c $script
EOS
}

$runner_fh->close;

my $error = qr{^(.*)\sat\s(.*)\sline\s(\d+)(\.|,\snear\s\".*\"?)$};
open my $fh, "sh $runner 2>&1 |" or die $!;
while (<$fh>) {
    print if $opt{verbose};
    chomp;
    if (my ($message, $extracted_file, $lineno, $rest) = /$error/) {
        next unless $extracted_file eq $script;
        $message .= $rest if $rest =~ s/^,//;
        print "$script:$lineno:$message\n";
    }
}
close $fh;

__END__

=head1 NAME

efm-perl - perl -c executable with errorformat friendly outputs.

=head1 SYNOPSIS

efm-perl [options]

    Options:
      --filename, -f [filename]    Filename to lint. This is mandatory.
      --lib, -I [paths]            Additional paths for $PERL5LIB.
      --verbose, -v                Print all outputs.
      --help, -h                   Show help message.

    # load the script from -f option
    efm-perl -f /path/to/script.pl

    # load the script from STDIN but filter out by filename from -f option
    cat /tmp/script.pl | efm-perl -f /path/to/script.pl

=head1 OPTIONS

=over 4

=item B<--lib>, B<-I>

Additional paths for C<PERL5LIB>

=item B<--filename>, B<-f>

Filename to lint. This is mandatory.

=item B<--verbose>, B<-v>

Print out all outputs. Without this, it shows errors only.

=item B<--help>, B<-h>

Print a help message.

=back

=head1 DESCRIPTION

This is a tiny script to use with L<mattn/efm-langserver|https://github.com/mattn/efm-langserver>.
It parses C<perl -c> outputs and arrange them to errorformat-friendly ones.

For efm-langserver, set config.yaml as below.

    tools:
      efm-perl: &efm-perl
        lint-command: efm-perl -f ${INPUT}
        lint-ignore-exit-code: true
        lint-stdin: true
        lint-formats:
          - '%f:%l:%m'

    languages:
      perl:
        - <<: *efm-perl

=head1 LICENSE

Copyright (C) delphinus.

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=head1 AUTHOR

delphinus <me@delphinus.dev>
