package IO::AIO::Util;

use strict;

use base qw(Exporter);
use IO::AIO 2;
use File::Spec::Functions qw(splitpath splitdir catpath catdir);

our $VERSION = '0.01';
our @EXPORT_OK = qw(aio_mkpath);

sub aio_mkpath ($$;$) {
    aio_block {
        my ($path, $mode, $cb) = @_;

        my $pri = aioreq_pri;
        my $grp = aio_group $cb;

        # Default is success.
        $grp->result(0);

        my @make;
        my $statgrp = add $grp aio_group sub {
            my $dirgrp = add $grp aio_group;
            for my $path (@make) {
                aioreq_pri $pri;
                add $dirgrp aio_mkdir $path, $mode, sub {
                    if ( $_[0] ) {
                        $grp->result( $_[0] );
                        $grp->errno($!);
                        return $grp->cancel_subs;
                    }
                };
            }
        };

        my ($vol, $dir, undef) = splitpath($path, 1);
        my @dirs = splitdir($dir);

        while (@dirs) {
            my $path = $path;

            aioreq_pri $pri;
            add $statgrp aio_stat $path, sub {
                # stat was successful
                if ( not $_[0] ) {
                    # fail if part of the expected path is not a dir
                    if ( ! -d _ ) {
                        $grp->result(-1);
                        $grp->errno($!);
                        return $grp->cancel_subs;
                    }
                    return $statgrp->cancel_subs;
                }
                # stat was not succesful, for reason other than non-existence
                elsif ( $_[0] and not $!{ENOENT} ) {
                    $grp->result(-1);
                    $grp->errno($!);
                    return $grp->cancel_subs;
                }

                unshift @make, $path;
            };
        }
        continue {
            pop @dirs;
            $path = catpath($vol, catdir(@dirs), '');
        }

        $grp;
    }
}
*aio_mktree = \&aio_mkpath;

1;

__END__

=head1 NAME

IO::AIO::Util - useful functions missing from IO::AIO

=head1 SYNOPSIS

    aio_mkpath "/tmp/dir1/dir2", 0755, sub {
        $_[0] and die "/tmp/dir1/dir2": $!";
    };

=head1 DESCRIPTION

This module provides useful functions that are missing from C<IO::AIO::Util>.

=head1 FUNCTIONS

=over 4

=item aio_mkpath $pathname, $mode, $callback->($status)

=item aio_mktree $pathname, $mode, $callback->($status)

This is a composite request that creates the directory and any intermediate
directories as required. The status is the same as aio_mkdir.

=back

=head1 SEE ALSO

L<IO::AIO|IO::AIO>

=head1 REQUESTS AND BUGS

Please report any bugs or feature requests to 
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=IO-AIO-Util>. I will 
be notified, and then you'll automatically be notified of progress on your bug 
as I make changes.

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc IO::AIO::Util

You can also look for information at:

=over 4

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/IO-AIO-Util>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/IO-AIO-Util>

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=IO-AIO-Util>

=item * Search CPAN

L<http://search.cpan.org/dist/IO-AIO-Util>

=back

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2007 gray <gray at cpan.org>, all rights reserved.

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

=head1 AUTHOR

gray, <gray at cpan.org>

=cut
