package Test::Stream::Plugin::SkipWithout;
use strict;
use warnings;

use Test::Stream::Context qw/context/;
use Test::Stream::Util qw/pkg_to_file/;
use Scalar::Util qw/reftype/;

use Test::Stream::Plugin;

sub load_ts_plugin {
    my $class = shift;
    my $caller = shift;
    for my $arg (@_) {
        if (ref $arg) {
            check_versions($caller, $arg);
        }
        elsif ($arg =~ m/^v?\d/) {
            check_perl_version($caller, $arg);
        }
        else {
            check_installed($caller, $arg);
        }
    }
}

sub skip {
    my ($msg) = @_;
    my $ctx = context();
    $ctx->plan(0, SKIP => $msg);
}

sub check_installed {
    my ($caller, $mod) = @_;
    my $file = pkg_to_file($mod);
    return if eval { require $file; 1 };
    my $error = $@;
    return skip("Module '$mod' is not installed")
        if $error =~ m/Can't locate \Q$file\E in \@INC/;

    # Some other error, rethrow it.
    die $error;
}

sub check_perl_version {
    my ($caller, $ver) = @_;
    return if eval "no warnings 'portable'; require $ver; 1";
    my $error = $@;
    if ($error =~ m/^(Perl \S* required)/i) {
        return skip($1);
    }

    # Other Error
    die $error;
}

sub check_versions {
    my ($caller, $ref) = @_;
    my $type = reftype($ref) || "";
    die "'$ref' is not a valid import argument to " . __PACKAGE__ . " at $caller->[1] line $caller->[2].\n"
        unless $type eq 'HASH';

    for my $mod (sort keys %$ref) {
        my $ver = $ref->{$mod};
        check_installed($caller, $mod);
        return if eval { $mod->VERSION($ver); 1 };
        chomp(my $error = $@);
        $error =~ s/ at .*$//;
        skip($error);
    }
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Test::Stream::Plugin::SkipWithout - Plugin to skip tests if certain package
requirements are not met.

=head1 EXPERIMENTAL CODE WARNING

B<This is an experimental release!> Test-Stream, and all its components are
still in an experimental phase. This dist has been released to cpan in order to
allow testers and early adopters the chance to write experimental new tools
with it, or to add experimental support for it into old tools.

B<PLEASE DO NOT COMPLETELY CONVERT OLD TOOLS YET>. This experimental release is
very likely to see a lot of code churn. API's may break at any time.
Test-Stream should NOT be depended on by any toolchain level tools until the
experimental phase is over.

=head1 DESCRIPTION

Sometimes you have tests that are nice to run, but depend on tools that may not
be available. Instead of adding the tool as a dep, or making the test always
skip, it is common to make the test run conditionally. This package helps make
that possible.

This module is modeled after L<Test::Requires>. This module even stole most of
the syntax. The difference is that this module is based on L<Test::Stream>
directly, and does not go through L<Test::Builder>. Another difference is that
the packages you check for are not imported into your namespace for you, this
is intentional.

=head1 SYNOPSIS

    use Test::Stream SkipWithout => [
        'v5.10',                 # minimum perl version
        'Necessary::Package',    # We need this, we do not care what version it is

        # A hashref can be used to specify modules + minimum versions
        {
            'Scalar::Util' => '1.3',    # We need at least this version of Scalar::Util
            'Some::Tool'   => '2.5',    # We need version 2.5 of Some::Tool
        },
    ];

    # The tools and features are not imported for us, so we import them here.
    # This gives us control over the import arguments as well.
    use v5.10;
    use Necessary::Package qw/foo bar/;
    use Scalar::Util qw/blessed reftype/;
    use Some::Tool qw/do_it/;

=head1 SOURCE

The source code repository for Test::Stream can be found at
F<http://github.com/Test-More/Test-Stream/>.

=head1 MAINTAINERS

=over 4

=item Chad Granum E<lt>exodist@cpan.orgE<gt>

=back

=head1 AUTHORS

=over 4

=item Chad Granum E<lt>exodist@cpan.orgE<gt>

=back

=head1 COPYRIGHT

Copyright 2015 Chad Granum E<lt>exodist7@gmail.comE<gt>.

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

See F<http://www.perl.com/perl/misc/Artistic.html>

=cut