Group
Extension

CPAN-Perl-Releases-MetaCPAN/lib/CPAN/Perl/Releases/MetaCPAN.pm

package CPAN::Perl::Releases::MetaCPAN;
use strict;
use warnings;

our $VERSION = '0.009';
use JSON::PP ();
use HTTP::Tinyish;

use Exporter 'import';
our @EXPORT_OK = qw(perl_tarballs perl_versions perl_pumpkins);

sub new {
    my ($class, %option) = @_;
    my $uri = $option{uri} || "https://fastapi.metacpan.org/v1/release";
    $uri =~ s{/$}{};
    my $cache = exists $option{cache} ? $option{cache} : 1;
    my $http = HTTP::Tinyish->new(verify_SSL => 1, agent => __PACKAGE__ . "/$VERSION");
    my $json = JSON::PP->new->canonical(1);
    bless { uri => $uri, http => $http, cache => $cache, json => $json }, $class;
}

sub get {
    my $self = shift;
    return $self->{_releases} if $self->{cache} and $self->{_releases};

    my @release;
    my $from = 0;
    my $total;
    my $uri = "$self->{uri}/_search";
    for (1..5) {
        # https://github.com/metacpan/metacpan-web/blob/master/lib/MetaCPAN/Web/Model/API/Release.pm
        # https://github.com/metacpan/metacpan-api/blob/master/lib/MetaCPAN/Document/Release/Set.pm
        my $query = {
            query => {
                bool => {
                    must => [
                        { term => { distribution => "perl" } },
                        { term => { authorized => JSON::PP::true } },
                    ],
                },
            },
            size => 1000,
            from => $from,
            sort => [ { date => 'desc' } ],
            fields => [qw( name date author version status maturity download_url checksum_sha256 )],
        };
        my $res = $self->{http}->post($uri, {
            content => $self->{json}->encode($query),
            headers => { 'content-type' => 'application/json' },
        });
        if (!$res->{success}) {
            my $message = $res->{status} == 599 ? ", $res->{content}" : "";
            chomp $message;
            $message =~ s/\n/ /g;
            die "$res->{status} $res->{reason}, $uri$message\n";
        }
        my $hash = $self->{json}->decode($res->{content});
        $total = $hash->{hits}{total} unless defined $total;
        push @release, map { $_->{fields} } @{$hash->{hits}{hits}};
        last if $total <= @release;
        $from = @release;
    }
    if ($total != @release) {
        die sprintf "metacpan returns %d perl releases, but expected %d\n",
            (scalar @release), $total;
    }
    $self->{_releases} = \@release if $self->{cache};
    \@release;
}

sub _self {
    my $self = eval { $_[0]->isa(__PACKAGE__) } ? shift : __PACKAGE__->new;
    wantarray ? ($self, @_) : $self;
}

sub perl_tarballs {
    my ($self, $arg) = _self @_;
    my $releases = $self->get;
    my %tarballs =
        map {
            my $url = $_->{download_url};
            $url =~ s{.*authors/id/}{};
            if ($url =~ /\.(tar\.\S+)$/) {
                ($1, $url);
            } else {
                ();
            }
        }
        grep { my $name = $_->{name}; $name =~ s/^perl-?//; $name eq $arg }
        grep { $_->{status} =~ /^(?:cpan|latest)$/ }
        @$releases;
    \%tarballs;
}

sub perl_versions {
    my $self = _self @_;
    my $releases = $self->get;
    my @versions =
        map { my $name = $_->{name}; $name =~ s/^perl-?//; $name }
        grep { $_->{status} =~ /^(?:cpan|latest)$/ }
        @$releases;
    @versions;
}

sub perl_pumpkins {
    my $self = _self @_;
    my $releases = $self->get;
    my %author =
        map { $_->{author} => 1 }
        grep { $_->{status} =~ /^(?:cpan|latest)$/ }
        @$releases;
    sort keys %author;
}

1;
__END__

=encoding utf-8

=head1 NAME

CPAN::Perl::Releases::MetaCPAN - Mapping Perl releases on CPAN to the location of the tarballs via MetaCPAN API

=head1 SYNOPSIS

  use CPAN::Perl::Releases::MetaCPAN;

  # Object oriented interface
  my $cpan = CPAN::Perl::Releases::MetaCPAN->new;
  my $releases = $cpan->get;
  # [
  #   {
  #     author => "BOOK",
  #     checksum_sha256 => "cb8715636bc744cca6e8864b6daa16f388d16ca3a134df6e6f35bbbe39dd7f63",
  #     date => "2023-12-30T21:59:20",
  #     download_url => "https://cpan.metacpan.org/authors/id/B/BO/BOOK/perl-5.39.6.tar.gz",
  #     maturity => "developer",
  #     name => "perl-5.39.6",
  #     status => "cpan",
  #     version => "5.039006",
  #   },
  #   ...
  # ]

  # Functions
  use CPAN::Perl::Releases::MetaCPAN qw/perl_tarballs/;

  my $hash = perl_tarballs('5.14.0');
  # {
  #   'tar.bz2' => 'J/JE/JESSE/perl-5.14.0.tar.bz2'
  # }

=head1 DESCRIPTION

CPAN::Perl::Releases::MetaCPAN is just like L<CPAN::Perl::Releases>,
but it gets the release information via MetaCPAN API C<https://fastapi.metacpan.org/v1/release>.

=head1 SEE ALSO

L<CPAN::Perl::Releases>

L<metacpan-api|https://github.com/metacpan/metacpan-api>

L<metacpan-web|https://github.com/metacpan/metacpan-web>

=head1 AUTHOR

Shoichi Kaji <skaji@cpan.org>

=head1 COPYRIGHT AND LICENSE

Copyright 2016 Shoichi Kaji <skaji@cpan.org>

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

=cut


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.