package File::ShareDir::Dist;

use strict;
use warnings;
use 5.008001;
use base qw( Exporter );
use File::Spec;

our @EXPORT_OK = qw( dist_share dist_config );

# ABSTRACT: Locate per-dist shared files
our $VERSION = '0.07'; # VERSION


# TODO: Works with PAR

our %over;

sub dist_share ($)
{
  my($dist_name) = @_;
  
  $dist_name =~ s/::/-/g;

  local $over{$1} = $2
    if defined $ENV{PERL_FILE_SHAREDIR_DIST} && $ENV{PERL_FILE_SHAREDIR_DIST} =~ /^(.*?)=(.*)$/;

  return File::Spec->rel2abs($over{$dist_name}) if $over{$dist_name};

  my @pm = split /-/, $dist_name;
  $pm[-1] .= ".pm";

  foreach my $inc (@INC)
  {
    my $pm = File::Spec->catfile( $inc, @pm );
    if(-f $pm)
    {
      my $share = File::Spec->catdir( $inc, qw( auto share dist ), $dist_name );
      if(-d $share)
      {
        return File::Spec->rel2abs($share);
      }
      
      if(!File::Spec->file_name_is_absolute($inc))
      {
        my($v,$dir) = File::Spec->splitpath( File::Spec->rel2abs($inc), 1 );
        my @dirs = File::Spec->splitdir($dir);
        if(defined $dirs[-1] && $dirs[-1] eq 'lib')
        {
          pop @dirs; # pop off the 'lib';
          # put humpty dumpty back together again
          my $share = File::Spec->catdir(
            File::Spec->catpath($v,
              File::Spec->catdir(@dirs),
              '',
            ),
            'share',
          );
          
          if(-d $share)
          {
            return $share;
          }
        }
      }

      last;
    }
  }
  
  return;
}


sub dist_config
{
  my($dist_name) = @_;
  my $dir = dist_share $dist_name;
  return {} unless defined $dir && -d $dir;
  my $fn = File::Spec->catfile($dir, 'config.pl');
  return {} unless -f $fn;
  my $fh;
  open($fh, '<', $fn) || die "unable to read $fn $!";
  my $pl = do { local $/; <$fh> };
  close $fh;
  my $config = eval $pl;
  die $@ if $@;
  $config;
}

sub import
{
  my($class, @args) = @_;

  my @modify;
  
  foreach my $arg (@args)
  {
    if($arg =~ /^-(.*?)=(.*)$/)
    {
      $over{$1} = $2;
    }
    else
    {
      push @modify, $arg;
    }
  }
  
  @_ = ($class, @modify);
  
  goto \&Exporter::import;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

File::ShareDir::Dist - Locate per-dist shared files

=head1 VERSION

version 0.07

=head1 SYNOPSIS

 use File::ShareDir::Dist qw( dist_share );
 
 my $dir = dist_share 'Foo-Bar-Baz';

=head1 DESCRIPTION

L<File::ShareDir::Dist> finds share directories for distributions.  It is similar to L<File::ShareDir>
with a few differences:

=over 4

=item Only supports distribution directories.

It doesn't support perl modules or perl class directories.  I have never really needed anything
other than a per-dist share directory.

=item Doesn't compute filenames.

Doesn't compute files in the share directory for you.  This is what L<File::Spec> or L<Path::Tiny>
are for.

=item Doesn't support old style shares.

For some reason there are two types.  I have never seen or needed the older type.

=item Hopefully doesn't find the wrong directory.

It doesn't blindly go finding the first share directory in @INC that matches the dist name.  It actually
checks to see that it matches the .pm file that goes along with it.

That does mean that you need to have a .pm that corresponds to your dist name.  This is not
always the case for some older historical distributions, but it has been the recommended convention
for quite some time.

=item No non-core dependencies.

L<File::ShareDir> only has L<Class::Inspector>, but since we are only doing per-dist share
directories we don't even need that.

The goal of this project is to have no non-core dependencies for the two most recent production
versions of Perl.  As of this writing that means Perl 5.26 and 5.24.  In the future, we C<may> add
dependencies on modules that are not part of the Perl core on older Perls.

=item Works in your development tree.

Uses the heuristic, for determining if you are in a development tree, and if so, uses the common
convention to find the directory named C<share>.  If you are using a relative path in C<@INC>,
if the directory C<share> is a sibling of that relative entry in C<@INC> and if the last element
in that relative path is C<lib>.

Example, if you have the directory structure:

 lib/Foo/Bar/Baz.pm
 share/data

and you invoke perl with

 % perl -Ilib -MFoo::Bar::Baz -MFile::ShareDir::Dist=dist_share -E 'say dist_share("Foo-Bar-Baz")'

C<dist_share> will return the (absolute) path to ./share/data.  If you invoked it with:

 % export PERL5LIB `pwd`/lib
 perl -MFoo::Bar::Baz -MFile::ShareDir::Dist=dist_share -E 'say dist_share("Foo-Bar-Baz")'

it would not.  For me this covers most of my needs when developing a Perl module with a share
directory.

L<prove> foils this heuristic by making C<@INC> absolute paths.  To get around that you can use
L<App::Prove::Plugin::ShareDirDist>.

=item Built in override.

The hash C<%File::ShareDir::Dist::over> can be used to override what C<dist_share> returns.
You can also override behavior on the command line using a dash followed by a key value pair
joined by the equal sign.  In other words:

 % perl -MFile::ShareDir::Dist=-Foo-Bar-Baz=./share -E 'say File::ShareDir::Dist::dist_share("Foo-Bar-Baz")'
 /.../share

If neither of those work then you can set PERL_FILE_SHAREDIR_DIST to a dist name, directory pair

 % env PERL_FILE_SHAREDIR_DIST=Foo-Bar-Baz=`pwd`/share perl -MFile::ShareDir::Dist -E 'say File::ShareDir::Dist::dist_share("Foo-Bar-Baz")'

For L<File::ShareDir> you have to either mock the C<dist_dir> function or install
L<File::ShareDir::Override>.  For testing you can use L<Test::File::ShareDir>.  I have never
understood why such a simple concept needs three modules to do all of this.

=back

=head1 FUNCTIONS

Functions must be explicitly exported.  They are not exported by default.

=head2 dist_share

 my $dir = dist_share $dist_name;
 my $dir = dist_share $module_name;

Returns the absolute path to the share directory of the given distribution.

As a convenience you can also use the "main" module name associated with the
distribution.  That means if you want the share directory for the dist
C<Foo-Bar-Baz> you may use either C<Foo-Bar-Baz> or C<Foo::Bar::Baz> to find
it.

Returns nothing if no share directory could be found.

=head2 dist_config

[version 0.07]

 my $config = dist_config $dist_name;

Returns the config at runtime as created by L<File::ShareDir::Dist::Install> and install time.

=head1 ENVIRONMENT

=over 4

=item PERL_FILE_SHAREDIR_DIST

Can be used to set a single dist directory override.

=back

=head1 CAVEATS

All the stuff that is in L<File::ShareDir> but not in this module could be considered either
caveats or features depending on your perspective I suppose.

=head1 SEE ALSO

=over

=item L<File::ShareDir::Dist::Install>

=item L<App::Prove::Plugin::ShareDirDist>

=item L<App::Yath::Plugin::ShareDirDist>

=back

=head1 AUTHOR

Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>

Contributors:

Yanick Champoux (yanick)

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017,2018 by Graham Ollis.

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