The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Net::FTP::Path::Iter;

# ABSTRACT: Iterative, recursive, FTP file finder

use 5.010;

use strict;
use warnings;
use Carp;

our $VERSION = '0.02';

use Net::FTP;
use File::Spec::Functions qw[ splitpath ];

use parent 'Path::Iterator::Rule';

use Net::FTP::Path::Iter::Dir;

use namespace::clean;

=method new

  $ftp = Net::FTP::Path::Iter->new( [$host], %options );

Open up a connection to an FTP host and log in.  The arguments
are the same as for L<Net::FTP/new>, with the addition of two
mandatory options,

=over

=item C<user>

The user name

=item C<password>

The password

=back

=cut


sub new {

    my $class = shift;

    my %attr;
    if (@_ % 2) {
        my $host = shift;
        %attr  = @_;
        $attr{Host} = $host;
    }
    else {
        %attr = @_;
    }

    my $self = $class->SUPER::new();

    defined( my $host = delete $attr{Host} )
      or croak( "missing Host attribute\n" );

    defined( my $user = delete $attr{user} )
      or croak( "missing user attribute\n" );

    defined( my $password = delete $attr{password} )
      or croak( "missing password attribute\n" );

    $self->{server} = Net::FTP->new($host, %attr)
      or croak("unable to connect to server $host\n");

    $self->{server}->login( $user, $password )
      or croak("unable to log in to $host\n");

    return $self;
}

sub _defaults {
    return (
        _stringify      => 0,
        follow_symlinks => 1,
        depthfirst      => 0,
        sorted          => 1,
        loop_safe       => 1,
        error_handler   => sub { die sprintf( "%s: %s", @_ ) },
        visitor         => undef,
    );
}

sub _fast_defaults {

    return (
        _stringify      => 0,
        follow_symlinks => 1,
        depthfirst      => -1,
        sorted          => 0,
        loop_safe       => 0,
        error_handler   => undef,
        visitor         => undef,
    );
}

sub _objectify {

    my ( $self, $path ) = @_;

    # on some FTP sites symlinked directories won't be properly listed
    # without a trailing slash.  listings won't work without a

    # for example, cdaftp.cfa.harvard.edu:/pub/byobsid/2/13182 is a symbolic link.
    # here's the listing :

    my ( $volume, $directories, $name ) = splitpath($path);

    # remove trailing slashes from directories; see splitpath docs
    $directories =~ s{(.+)/$}{$1};

    my %attr = (
        parent  => $directories,
        name => $name,
        path => $path,
    );

    return Net::FTP::Path::Iter::Dir->new( server => $self->{server}, %attr );
}

sub _children {

    my ( $self, $path ) = @_;

    return map { [ $_->{name}, $_ ] } $path->_children;
}

sub _iter {

    my $self     = shift;
    my $defaults = shift;

    $defaults->{loop_safe} = 0;

    $self->SUPER::_iter( $defaults, @_ );

}

1;

# COPYRIGHT

__END__

=head1 SYNOPSIS

    use Net::FTP::Path::Iter;

    # connect to the FTP site
    my $ftp = Net::FTP::Path::Iter->new( $ftp_site, $user, $password );

    # define a visitor callback routine. It will recieve a
    # Net::FTP::Path::Iter::Entry object.
    sub visitor { my ($entry) = @_ }

    # use the Path::Iterator::Rule all() method to traverse the
    # site;
    $ftp->all( '/', \&visitor );

=head1 DESCRIPTION

B<Net::FTP::Path::Iter> is a subclass of L<Path::Iterator::Rule> which
iterates over an FTP site rather than a local filesystem.

See the documentation L<Path::Iterator::Rule> for how to filter and
traverse paths.  When B<Net::FTP::Path::Iter> passes a path to a callback or
returns one from an iterator, it will be in the form of a
L<Net::FTP::Path::Iter::Entry> object.

B<Net::FTP::Path::Iter> uses L<Net::FTP> to connect to the FTP site.

=head2 Symbolic Links

At present, B<Net::FTP::Path::Iter> does not handle symbolic links. It will
output an error and skip them.


=head1 ATTRIBUTES

B<Net::FTP::Path::Iter> subclasses L<Path::Iter::Rule>. It is a hash based object
and has the following additional attributes:

=over

=item C<server>

The B<Net::FTP> object representing the connection to the FTP server.

=back

=head1 SEE ALSO