package DataStore::CAS::FS::Dir;
use 5.008;
use strict;
use warnings;
use Carp;
use Try::Tiny;

our $VERSION= 0.0100;

# ABSTRACT: Object representing a directory of file entries, indexed by filename.


sub file     { $_[0]{file} }
sub store    { $_[0]{file}->store }
sub hash     { $_[0]{file}->hash }
sub size     { $_[0]{file}->size }

sub format   { $_[0]{format} }

sub metadata { $_[0]{metadata} } 


sub new {
	my $class= shift;
	my %p= (1 == @_ && ref $_[0] eq 'HASH')? %{$_[0]} : @_;
	defined $p{file} or croak "Attribute 'file' is required";
	defined $p{format} or croak "Attribute 'format' is required";
	$p{metadata} ||= {};
	$p{_entries}= delete $p{entries} || [];
	bless \%p, $class;
}


sub iterator {
	my $list= $_[0]{_entries};
	my ($i, $n)= (0, scalar @$list);
	return sub { $i < $n? $list->[$i++] : undef };
}

sub get_entry {
	my ($self, $name, $flags)= @_;
	return $flags->{case_insensitive}?
		($self->{_entry_name_map_caseless} ||= do {
			my (%lookup, $ent, $iter);
			for ($iter= $self->iterator; defined ($ent= $iter->()); ) {
				$lookup{uc $ent->name}= $ent
			}
			\%lookup;
		})->{uc $name}
		:
		($self->{_entry_name_map} ||= do {
			my (%lookup, $ent, $iter);
			for ($iter= $self->iterator; defined ($ent= $iter->()); ) {
				$lookup{$ent->name}= $ent
			}
			\%lookup;
		})->{$name};
}

1;

__END__

=pod

=head1 NAME

DataStore::CAS::FS::Dir - Object representing a directory of file entries, indexed by filename.

=head1 VERSION

version 0.0100

=head1 SYNOPSIS

  my $dir= DataStore::CAS::FS::Dir->new(
    file => $cas_file,
    format => $codec_name,
    entries => \@entries,
    metadata => $metadata
  );

=head1 DESCRIPTION

Directory objects have a very basic API of being able to fetch an entry by
name (optionally case-insensitive, as the user chooses), and iterate all
entries.

Directory objects are B<IMMUTABLE>, as are the L<DirEnt|DataStore::CAS::FS::DirEnt> objects they return.

=head1 ATTRIBUTES

=head2 file

Read-only, Required.  The L<DataStore::CAS::File> this directory was deserialized
from.

=head2 store

Alias for file->store

=head2 hash

Alias for file->hash

=head2 size

Alias for file->size

=head2 format

The format string that identifies this directory encoding.

=head2 metadata

A hashref of arbitrary name/value pairs attached to the directory at the time
it was written.  DO NOT MODIFY.  (In the future, this might be protected by
Perl's internal const mechanism)

=head1 METHODS

=head2 new

  $dir= $class->new( %params | \%params )

Create a new basic Dir object.  The required parameters are C<file>, and
C<format>.  C<metadata> will default to an empty hashref, and C<entries> will
default to an empty list.

The C<entries> parameter is not a public attribute, and is stored internally
as C<_entries>.  This is because not all subclasses will have an array of
entries available.  Use the method C<iterator> instead.

=head2 iterator

  $i= $dir->iterator;
  while (my $next= $i->()) { ... }

Returns an iterator over the entries in the directory.

The iterator is a coderef where each successive call returns the next L<DirEnt|DataStore::CAS::FS::DirEnt>.
Returns undef at the end of the list. Entries are not guaranteed to be in any
order, or even to be unique names.  (in particular, because of case
sensitivity rules)

=head2 get_entry

  $dirEnt= $dir->get_entry($name, %flags)

Get a directory entry by name.

If C<$flags{case_insensitive}> is true, then the directory will attempt to do a
case-folding lookup on the given name.  Note that all directories are
case-sensitive when written, and the case-insensitive feature is meant to help
emulate Windows-like behavior.  In other words, you might have two entries
that differ only by case, and the caseless lookup will pick one arbitrarily.

=head1 AUTHOR

Michael Conrad <mconrad@intellitree.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2013 by Michael Conrad, and IntelliTree Solutions llc.

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

=cut
