The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl
# -*- Mode: cperl-mode; cperl-indent-level: 4 -*-

# $Id: /local/CPAN/SVN-Log-Index/trunk/bin/sli 1474 2007-01-13T21:14:25.326886Z nik  $

use strict;
use warnings;

use SVN::Log::Index;

use Getopt::Long;
use Pod::Usage;

=head1 NAME

sli - Build and query SVN::Log::Index indices

=head1 SYNOPSIS

B<sli> I<command> S<[I<options>]> [I<args>]

=head1 DESCRIPTION

sli is a command line interface to L<SVN::Log::Index>.  It supports adding
revisions to an index, and then querying that index for revisions that
match the query.

Each index remembers the URL of the Subversion repository that it is
indexing and the revision number that was last successfully indexed.

The command line follows the Subversion/SVK model.  There may be some
global options, followed by a command name.  The command may then require
a number of other options to be specified.

=head1 GLOBAL OPTIONS

=over

=item --index PATH

The full path to the index to operate on.  If not specified then the
default value is ${HOME}/.sli/index/.

=back

=head1 COMMANDS

=cut

my %options;

my %cmds = (create => \&do_create,
	    add    => \&do_add,
	    search => \&do_search,
	    help   => \&do_help,
	    );

$options{index}     = "$ENV{HOME}/.sli/index";
$options{overwrite} = 0;
$options{verbose}   = 0;

GetOptions('index=s'     => \$options{index},
	   'repos=s'     => \$options{repos},
	   'overwrite'   => \$options{overwrite},
	   'revision=s'  => \$options{revision},
	   'query=s'     => \$options{query},
	   'verbose'     => \$options{verbose},
	  ) or pod2usage(-verbose => 2);

my $cmd = shift;

pod2usage(-verbose => 2) if ! defined $cmd;
$cmd = 'help' if ! exists $cmds{$cmd};

$cmds{$cmd}->(\%options);

exit;

=head2 create

Creates a new index.  The options and their meanings are:

=over

=item --repos URL

This option is mandatory.

The URL of the Subversion repository that will be indexed.  Only one
repository can be indexed per C<--index>.  This option is mandatory.

=item --overwrite

If present then the C<--index> will be overwritten.  If not present
then the existence of any files in C<--index> will cause the command
to stop.

=back

=cut

sub do_create {
    my $o = shift;

    my $index = SVN::Log::Index->new ({ index_path => $options{index} });

    $index->create({ repo_url  => $o->{repos},
		     overwrite => $o->{overwrite} });

}

=head2 add

Adds information about one or more revisions to an existing index.  The
options and their meanings are:

=over

=item --revision N or --revision N:M

This option is mandatory.

The revision, or revisions, to be added to the index.  If a single
revision number is given then just that revision is added to the index.
If two revision numbers are given, then information about all revisions
between those two numbers, inclusive, are added to the index.

Two symbolic revision names may be used instead of numbers.  C<LAST>
means the revision number one higher than the revision number last
indexed.  C<HEAD> means the repository's youngest revision.

=back

=cut

sub do_add {
    my $o = shift;
    my $index = SVN::Log::Index->new ({ index_path => $options{index} });

    $index->open();

    $o->{revision} =~ s/LAST/$index->get_last_indexed_rev() + 1/e;

    if($o->{revision} !~ /:/) {
	$index->add({ start_rev => $o->{revision} });
    } else {
	my ($startrev, $endrev) = $o->{revision} =~ m/^([^:]+):([^:]+)$/;

	$index->add({ start_rev => $startrev, end_rev => $endrev });
    }
}

=head2 search

Search the repository, generating a report of all the revisions that
matched the query.  The options and their meanings are:

=over

=item --query QUERY

This option is mandatory.

The query string to search for.  See the documentation for
L<SVN::Log::Index/"QUERY SYNTAX"> for details of the query string syntax.

=item --verbose

Boolean, indicating whether the report should include each matching
revision's log message.

=back

=cut

sub do_search {
    my $o = shift;
    my $index = SVN::Log::Index->new({ index_path => $o->{index} });

    $index->open();

    my $hits = $index->search($o->{query});

    my $total = $hits->total_hits();

    while(my $result = $hits->fetch_hit_hashref()) {
	printf "%s\n", "-----" x 15 if $o->{verbose};

	printf "r%-8d | %-8s | %s\n",
	    $result->{revision}, $result->{author}, $result->{date};

	my $msg = $result->{message}; $msg =~ s/^\n*//; $msg =~ s/\n*$//;

	printf "\n%s\n\n", $msg if $o->{verbose};
	print "Relevance: $result->{score}\n" if $o->{verbose};
    }
    printf "%s\n", "-----" x 15 if $o->{verbose} and $total;
}

=head2 help

Displays help about each command.

The available commands are:

  create        - create a new index
  add           - add one or more revisions to the index
  search        - search the index
  help          - display this help

Use

  sli help <command>

for more.

=cut

sub do_help {
    my $subcmd = shift @ARGV;
    $subcmd = 'help' unless defined $subcmd;
    $subcmd = 'help' if ! exists $cmds{$subcmd};

    pod2usage(-verbose => 99,
	      -sections => 'SYNOPSIS|GLOBAL OPTIONS|COMMANDS/!.+',
	      -exitval => 'NOEXIT');
    pod2usage(-verbose => 99,
	      -sections => "COMMANDS/$subcmd");
}

exit 0;

=head1 EXAMPLES

Create a new index.

  sli create --index /path/to/index --repos url://for/repos

Add the first 100 revisions to the index.

  sli add --index /path/to/index --revision 1:100

Add the next revision to the index.

  sli add --index /path/to/index --revision 101

Add up to revision 150 to the index.

  sli add --index /path/to/index --revision LAST:150

Add all the remaining revisions to the index.

  sli add --index /path/to/index --revision LAST:HEAD

Search the index for commits by C<nik> that contain the string C<fixed>.
Show basic information about the revision.

  sli search --index /path/to/index --query "author:nik AND fixed"

Repeat the previous query, but use C<+> to connect terms.

  sli search --index /path/to/index --query "author:nik +fixed"

Repeat the previous query, but include each revision's log message
too.

  sli search --index /path/to/index --query "author:nik AND fixed" \
    --verbose

=head1 SEE ALSO

L<SVN::Log::Index>, L<KinoSearch::QueryParser::QueryParser>

=head1 BUGS

Please report any bugs or feature requests to
C<bug-svn-log-index@rt.cpan.org>, or through the web interface at
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=SVN-Log-Index>.
I will be notified, and then you'll automatically be notified of progress on
your bug as I make changes.

=head1 AUTHOR

The current maintainer is Nik Clayton, <nikc@cpan.org>.

The original author was Garrett Rooney, <rooneg@electricjellyfish.net>

=head1 COPYRIGHT AND LICENSE

Copyright 2006-2007 Nik Clayton.  All Rights Reserved.

Copyright 2004 Garrett Rooney.  All Rights Reserved.

This software is licensed under the same terms as Perl itself.

=cut