App-cpan2arch

 view release on metacpan or  search on metacpan

lib/App/cpan2arch/GetMetadata.pm  view on Meta::CPAN

use v5.42.0;

use strict;
use warnings;
no source::encoding;  # Avoid dying on v5.42.0 (non-ASCII char in POD).

use Object::Pad 0.825;

package App::cpan2arch::GetMetadata;  # For toolchain compatibility.
role App::cpan2arch::GetMetadata;

use File::Spec::Functions qw< catdir splitdir >;
use Scalar::Util          qw< looks_like_number >;

our $VERSION = 'v1.1.2';

field %_endpoints :reader :writer = (
    module   => 'https://fastapi.metacpan.org/v1/module/',
    release  => 'https://fastapi.metacpan.org/v1/release/',
    download => 'https://fastapi.metacpan.org/v1/download_url/',
);
field $_mua_mcpan :reader;
field %_optionals :reader;
field %_meta      :reader :writer;

# Get CPAN metadata from MetaCPAN's API.
#
# References:
#   https://github.com/metacpan/metacpan-api/blob/master/docs/API-docs.md
method get_metadata ()
{
    $self->_psub;

    $self->_init_mua_mcpan;

    # Get the module/distribution and its release.
    my $dist;
    my $rel;
    {
        my %args    = $self->args;
        my $module  = $args{module};
        my $version = $args{version};
        my $mod;

        # Only request for a module if it does not look like a dist and no version
        # argument is passed, otherwise treat it as dist.
        if ( $module !~ /-/ && !defined $version ) {
            $mod = $self->_get_module($module);
            $self->_pdbg("found module\n\n") if defined $mod;
        }

        # Since modules and dists names can be ambiguous, e.g. Reply, do not exit
        # if a module request fails, but fallback as dist.
        $dist = defined $mod ? $mod->{distribution} : $module;

        $self->_pdbg("Dist\n");
        $self->_pdump( '$dist', \$dist, "\n" );

        $rel = $self->_get_release($dist);
        return 1 if $rel == 1;

        $self->_pdbg("Release\n");
        $self->_pdump( '$rel', \$rel, "\n" );
    }

    # Find Module::Install, license, and XS files in the dist.
    my $ret = $self->_find_files( $dist, $rel->{download_url} );
    return 1 if looks_like_number($ret) && $ret == 1;
    my %files = $ret->%*;

    # Get 'optionals_features' descriptions (will be added to the optdepends array).
    # See https://metacpan.org/pod/CPAN::Meta::Spec#optional_features.
    {
        foreach my ( $feature, $feat_info ) ( $rel->{metadata}{optional_features}->%* ) {

            foreach my ( $phase, $phase_info ) ( $feat_info->{prereqs}->%* ) {

lib/App/cpan2arch/GetMetadata.pm  view on Meta::CPAN

            }
        }
    };

    my %env  = $self->env;
    my %opts = $self->opts;
    my $mua;

    if ( defined $has_muac && defined $has_chi ) {
        require Mojo::Log;

        # Silence logger
        my $logger;
        $logger = Mojo::Log->new( path => '/dev/null' ) unless $env{debug};

        $mua = Mojo::UserAgent::Cached->new(
            $env{debug}
            ? ()
            : ( logger => $logger ),
        );

        # Use CHI as the cache backend.
        {
            my $path =
                $type eq 'mcpan'
              ? $env{cache_mcpan_path}
              : $env{cache_arch_path};

            my $chi;

            $chi = CHI->new(
                driver     => 'File',
                root_dir   => $path,
                expires_in => $env{cache_expiration},
            ) unless $env{cache_ignore};

            if ( defined $chi ) {
                $chi->clear
                  if defined $opts{clear}
                  || ( $type eq 'mcpan' && defined $opts{clear_mcpan} )
                  || ( $type eq 'arch'  && defined $opts{clear_arch} );
            }

            $mua->cache_agent($chi) unless $env{cache_ignore};
        }
    }
    else {
        $mua = Mojo::UserAgent->new;
    }

    $mua->transactor->name( $env{user_agent} ) if defined $mua;

    return $mua;
}

method _get_module ($module)
{
    $self->_psub;

    my $prog = $self->prog;
    my $url  = $_endpoints{module} . "$module?fields=distribution";
    my $json;

    my $res = do {
        try {
            my %env = $self->env;
            local $ENV{MUAC_NOCACHE} = true if $env{cache_ignore};

            $_mua_mcpan->get($url)->result;
        }
        catch ($e) {
            warn $e;
            undef;
        }
    };

    if ( defined $res && $res->is_success ) {
        $self->_pdbg("\n");
        $json = $res->json;
    }

    if ( !defined $json ) {
        warn "$prog: failed to fetch $module module\n";
        $self->_pdbg("\n");

        return undef;
    }

    return $json;
}

# References:
#   https://blogs.perl.org/users/neilb/2016/12/working-with-the-metacpan-api.html.
method _get_release ($dist)
{
    $self->_psub;

    my %args    = $self->args;
    my $version = $args{version};
    my $prog    = $self->prog;

    my $query = "distribution:$dist%20AND%20";
    $query
      .= defined $version
      ? "version:$version"
      : 'status:latest';

    my $url = $_endpoints{release} . "_search?q=$query";

    my $res = do {
        try {
            my %env = $self->env;
            local $ENV{MUAC_NOCACHE} = true if $env{cache_ignore};

            $_mua_mcpan->get($url)->result;
        }
        catch ($e) {
            warn $e;
            undef;
        }
    };

    my $json;
    my $rel;

    if ( defined $res && $res->is_success ) {
        $self->_pdbg("\n");

        $json = $res->json;
        $rel  = $json->{hits}{hits}[0]{_source}
          if defined $json && scalar $json->{hits}{hits}->@*;
    }

    if ( !defined $json || !defined $rel ) {
        warn "$prog: failed to fetch $dist dist release\n";
        return 1;
    }

    return $rel;
}

# Check if the distribution has some type of files (M::I, license, XS).
method _find_files ( $dist, $download_url )
{
    $self->_psub;

    require Path::Tiny;
    Path::Tiny->VERSION('0.150');

    require Archive::Tar;

    my $prog  = $self->prog;
    my %files = (
        mi                 => false,
        license            => false,
        has_multi_licenses => false,
        xs                 => false,
    );

    my $res = do {
        try {
            my %env = $self->env;
            local $ENV{MUAC_NOCACHE} = true if $env{cache_ignore};

            $_mua_mcpan->get($download_url)->result;
        }
        catch ($e) {
            warn $e;



( run in 1.668 second using v1.01-cache-2.11-cpan-524268b4103 )