package App::GenBashCompleter;

our $DATE = '2015-09-03'; # DATE
our $VERSION = '0.02'; # VERSION

use 5.010001;
use strict;
use warnings;
#use Log::Any '$log';

use Data::Dump qw(dump);

our %SPEC;

sub _gen_gl {
    my $spec = shift;

    my @res;

    for (keys %$spec) {
        $spec->{$_} = sub{};
    }

    push @res, "use Getopt::Long::Complete qw(GetOptionsWithCompletion);\n";
    push @res, "my \@spec = ", dump(%$spec), ";\n";
    push @res, "GetOptionsWithCompletion(sub{}, \@spec);\n";
    join "", @res;
}

$SPEC{gen_bash_completer} = {
    v => 1.1,
    summary => 'Generate bash completion script for a program',
    args => {
        path => {
            summary => 'Path to program/script to generate completion for',
            description => <<'_',

You can specify `-` to mean stdin.

_
            schema => 'str*',
            req => 1,
            pos => 0,
        },
    },
    result => {
        summary => 'Will return bash completer script, or undef',
        schema => 'buf*',
    },
};
sub gen_bash_completer {
    my %args = @_;

    my $path = $args{path};
    my $content;
    {
        local $/;
        if ($path eq '-') {
            $content = <STDIN>;
        } else {
            open my($fh), "<", $path
                or return [500, "Can't open file '$path': $!"];
            $content = <$fh>;
        }
    }

    my $reason;
    my $shebang;
    my $completer;
  DETECT:
    {
        if ($content !~ /\A(#!.+)/) {
            $reason = 'Not a script (no shebang line)';
            last DETECT;
        }
        $shebang = $1;
        if ($shebang !~ /.+\bperl\b/) {
            $reason = 'Shebang line does not indicate this is a Perl script';
            last DETECT;
        }
        if ($content =~ /(use|require)\s+
                         (Getopt::Long::Complete|
                             Perinci::CmdLine(::Any|::Lite)?)\b/sx) {
            return [304, "Script using $1 can complete itself"];
        }
        if ($content =~ /(use|require)\s+(Getopt::Long)\b/sx) {
            require Capture::Tiny;
            require UUID::Random;
            my $tag = UUID::Random::generate();
            my ($stdout, $stderr, $exit) = Capture::Tiny::capture(
                sub {
                    system $^X,
                        "-MGetopt::Long::Patch::DumpAndExit=-tag,$tag",
                        $path;
                },
            );
            if ($stdout =~ /^# BEGIN DUMP $tag\s+(.*)^# END DUMP $tag/ms) {
                my $spec = eval $1;
                if ($@) {
                    $reason = "Detected as Perl script using Getopt::Long, ".
                        "but error in eval-ing captured option spec: $@, ".
                            "raw captured option spec: <<<$1>>>";
                     last DETECT;
                }
                if (ref($spec) ne 'HASH') {
                    $reason = "Detected as Perl script using Getopt::Long, ".
                        "but got a non-hash option spec?";
                }
                $completer = _gen_gl($spec);
                last DETECT;
            } else {
                $reason = "Detected as Perl script using Getopt::Long, ".
                    "but can't capture option spec";
                last DETECT;
            }
        }
        $reason = "No completion hints available";
    } # DETECT

    if ($completer) {
        my @res = ($shebang, "\n");

        push @res, "# generated by $0 on " . localtime . "\n";

        push @res, "# FRAGMENT id=bash-completion-prog-hints completer=1";
        if ($path ne '-') {
            my $progname = $path; $progname =~ s!.+[/\\]!!;
            push @res, " for=$progname";
        }
        push @res, "\n";

        push @res, $completer;
        $completer = join "", @res;
    }

    return [200, "OK", $completer, {'func.reason' => $reason}];
}

1;
# ABSTRACT: Generate bash completion script for a program

__END__

=pod

=encoding UTF-8

=head1 NAME

App::GenBashCompleter - Generate bash completion script for a program

=head1 VERSION

This document describes version 0.02 of App::GenBashCompleter (from Perl distribution App-GenBashCompleter), released on 2015-09-03.

=head1 FUNCTIONS


=head2 gen_bash_completer(%args) -> [status, msg, result, meta]

Generate bash completion script for a program.

Arguments ('*' denotes required arguments):

=over 4

=item * B<path>* => I<str>

Path to program/script to generate completion for.

You can specify C<-> to mean stdin.

=back

Returns an enveloped result (an array).

First element (status) is an integer containing HTTP status code
(200 means OK, 4xx caller error, 5xx function error). Second element
(msg) is a string containing error message, or 'OK' if status is
200. Third element (result) is optional, the actual result. Fourth
element (meta) is called result metadata and is optional, a hash
that contains extra information.

Return value: Will return bash completer script, or undef (buf)

=head1 HOMEPAGE

Please visit the project's homepage at L<https://metacpan.org/release/App-GenBashCompleter>.

=head1 SOURCE

Source repository is at L<https://github.com/perlancar/perl-App-GenBashCompleter>.

=head1 BUGS

Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=App-GenBashCompleter>

When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.

=head1 AUTHOR

perlancar <perlancar@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by perlancar@cpan.org.

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

=cut
