The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package File::UnixAuth::Storage;

use namespace::autoclean;

use File::DataClass::Constants qw( FALSE NUL SPC TRUE );
use Moo;

extends q(File::DataClass::Storage);

# Private functions
my $_original_order = sub {
   my ($hash, $lhs, $rhs) = @_;

   # New elements will be  added at the end
   exists $hash->{ $lhs }->{_order_by} or return  1;
   exists $hash->{ $rhs }->{_order_by} or return -1;
   return $hash->{ $lhs }->{_order_by} <=> $hash->{ $rhs }->{_order_by};
};

my $_parse_name = sub {
   my $full_name = shift; $full_name or return {};

   my ($first_name, $last_name) = split SPC, $full_name, 2;

   return { first_name => $first_name, last_name => $last_name };
};

# Private methods
my $_deflate = sub {
   my ($self, $hash, $id) = @_; my $attr = $hash->{ $id }; my $gecos = NUL;

   exists $attr->{members   }
      and $attr->{members   } = join ',', @{ $attr->{members   } || [] };
   exists $attr->{first_name} and $gecos .=  $attr->{first_name} // NUL;
   exists $attr->{last_name } and $gecos .=  $attr->{last_name }
                                       ? SPC.$attr->{last_name }  : NUL;

   if ($attr->{location} or $attr->{work_phone} or $attr->{home_phone}) {
      $gecos .= ','.($attr->{location  } // NUL);
      $gecos .= ','.($attr->{work_phone} // NUL);
      $gecos .= ','.($attr->{home_phone} // NUL);
   }

   $gecos and $attr->{gecos} = $gecos;
   return;
};

my $_inflate = sub {
   my ($self, $hash, $id) = @_; my $attr = $hash->{ $id };

   exists $attr->{members}
      and $attr->{members} = [ split m{ , }mx, $attr->{members} // NUL ];

   if (exists $attr->{gecos}) {
      my @fields = qw( full_name location work_phone home_phone );

      @{ $attr }{ @fields } = split m{ , }mx, $attr->{gecos} // NUL;

      my $names  = $_parse_name->( $attr->{full_name} );

      $attr->{first_name} = $names->{first_name} // NUL;
      $attr->{last_name } = $names->{last_name } // NUL;
      delete $attr->{full_name}; delete $attr->{gecos};
   }

   return;
};

my $_read_filter = sub {
   my ($self, $buf) = @_; my $hash = {}; my $order = 0;

   my $source_name = $self->schema->source_name;
   my $fields      = $self->schema->source->attributes;

   for my $line (@{ $buf || [] }) {
      my ($id, @rest) = split m{ : }mx, $line; my %attr = ();

      @attr{ @{ $fields } } = @rest;
      $attr{ _order_by    } = $order++;
      $hash->{ $id } = \%attr;
      $self->$_inflate( $hash, $id );
   }

   return { $source_name => $hash };
};

my $_write_filter = sub {
   my ($self, $data) = @_; my $buf = [];

   my $source_name = $self->schema->source_name;
   my $fields      = $self->schema->source->attributes;
   my $hash        = $data->{ $source_name };

   $source_name eq 'passwd' and $fields = [ @{ $fields }[ 0 .. 5 ] ];

   for my $id (sort { $_original_order->( $hash, $a, $b ) } keys %{ $hash }) {
      $self->$_deflate( $hash, $id );

      my $attr = $hash->{ $id }; delete $attr->{_order_by};
      my $line = join ':', map { $attr->{ $_ } // NUL } @{ $fields };

      push @{ $buf }, "${id}:${line}";
   }

   return $buf;
};

# Public methods
sub read_from_file {
   my ($self, $rdr) = @_;

   $self->encoding and $rdr->encoding( $self->encoding );

   return $self->$_read_filter( [ $rdr->chomp->getlines ] );
}

sub write_to_file {
   my ($self, $wtr, $data) = @_;

   $self->encoding and $wtr->encoding( $self->encoding );
   $wtr->println( @{ $self->$_write_filter( $data ) } );

   return $data;
};

1;

__END__

=pod

=head1 Name

File::UnixAuth::Storage - Unix authentication and authorisation file storage

=head1 Synopsis

   use File::UnixAuth::Storage;

=head1 Description

=head1 Configuration and Environment

=head1 Subroutines/Methods

=head2 read_from_file

=head2 write_to_file

=head1 Diagnostics

=head1 Dependencies

=over 3

=item L<File::DataClass::Storage>

=item L<MooX::Augment>

=back

=head1 Incompatibilities

There are no known incompatibilities in this module

=head1 Bugs and Limitations

There are no known bugs in this module.
Please report problems to the address below.
Patches are welcome

=head1 Author

Peter Flanigan, C<< <Support at RoxSoft.co.uk> >>

=head1 License and Copyright

Copyright (c) 2015 Peter Flanigan. All rights reserved

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself. See L<perlartistic>

This program is distributed in the hope that it will be useful,
but WITHOUT WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE

=cut

# Local Variables:
# mode: perl
# tab-width: 3
# End: