The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package App::KGB::Client::Subversion;
use utf8;

# vim: ts=4:sw=4:et:ai:sts=4
#
# KGB - an IRC bot helping collaboration
# Copyright © 2008 Martín Ferrari
# Copyright © 2009,2010 Damyan Ivanov
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

use strict;
use warnings;

our $VERSION = 1.27;

=head1 NAME

App::KGB::Client::Subversion - KGB interface to Subversion

=head1 SYNOPSIS

    use App::KGB::Client::Subversion;
    my $client = App::KGB::Client::Subversion(
        # common App::KGB::Client parameters
        repo_id => 'my-repo',
        ...
        # Subversion-specific
        repo_path   => '/svn/project',
        revision    => 42,
    );
    $client->run;

=head1 DESCRIPTION

B<App::KGB::Client::Subversion> provides Subversion-specific retrieval of
commits and changes for L<App::KGB::Client>.

=head1 CONSTRUCTOR

=head2 B<new> ( { initializers } )

Standard constructor. Accepts inline hash with initial field values.

=head1 FIELDS

App:KGB::Client::Subversion defines two additional fields:

=over

=item B<repo_path> (B<mandatory>)

Physical path to Subversion repository.

=item B<revision>

The revision about which to notify. If omitted defaults to the last revision
of the repository.

=back

=head1 METHODS

=over

=item describe_commit

The first time this method is called, it retrieves commit number and repository
path from command-line parameters and returns an instance of
L<App::KGB::Commit> class describing the commit.

All subsequential invocations return B<undef>.

=back

=cut

require v5.10.0;
use base 'App::KGB::Client';
use App::KGB::Change;
use App::KGB::Commit;
use Carp qw(confess);
use SVN::Core;
use SVN::Fs;
use SVN::Repos;
__PACKAGE__->mk_accessors(qw( _called repo_path revision ));

use constant rev_prefix => 'r';

sub new {
    my $class = shift;
    my $self = $class->SUPER::new(@_);
    $self->_called(0);

    defined( $self->repo_path )
        or confess "'repo_path' is mandatory";

    return $self;
}

sub describe_commit {
    my ($self) = @_;

    return undef if $self->_called;

    my ( $author, $log, @changes );

    # Shut up the perl compiler warnings
    if (    $SVN::Fs::PathChange::modify
        and $SVN::Fs::PathChange::add
        and $SVN::Fs::PathChange::delete
        and $SVN::Fs::PathChange::replace )
    {
    }

    my $repo = SVN::Repos::open( $self->repo_path );
    my $fs = $repo->fs or die $!;

    $self->revision( $fs->youngest_rev ) unless defined( $self->revision );
    $log    = $fs->revision_prop( $self->revision, "svn:log" );
    $author = $fs->revision_prop( $self->revision, "svn:author" );

    my $root    = $fs->revision_root( $self->revision );
    my $changed = $root->paths_changed();
    foreach ( keys %$changed ) {
        my $k = $changed->{$_}->change_kind();
        if ( $k == $SVN::Fs::PathChange::modify ) {
            $k = "M";
        }
        elsif ( $k == $SVN::Fs::PathChange::add ) {
            $k = "A";
        }
        elsif ( $k == $SVN::Fs::PathChange::delete ) {
            $k = "D";
        }
        elsif ( $k == $SVN::Fs::PathChange::replace ) {
            $k = "R";
        }

        my $pm = $changed->{$_}->prop_mod();

        push @changes,
            App::KGB::Change->new(
            {   action      => $k,
                prop_change => $pm,
                path        => $_,
            }
            );
    }

    $self->_called(1);

    return App::KGB::Commit->new(
        {   id      => $self->revision,
            changes => \@changes,
            author  => $author,
            author_name => $self->_get_full_user_name($author),
            log     => $log,
        }
    );
}

1;