package App::envfile;

use strict;
use warnings;
use 5.008_001;
use Carp ();

our $VERSION = '0.05';

our $EXTENTIONS_MAP = {
    pl   => 'Perl',
    perl => 'Perl',
    js   => 'JSON',
    json => 'JSON',
    yml  => 'YAML',
    yaml => 'YAML',
};

sub new {
    my $class = shift;
    bless {}, $class;
}

sub run_with_env {
    my ($self, $env, $commands) = @_;
    local %ENV = %ENV;
    for my $key (keys %$env) {
        $ENV{$key} = $env->{$key};
    }
    exec(@$commands);
}

sub parse_envfile {
    my ($self, $file) = @_;
    Carp::croak "Usage: $self->parse_envfile(\$file)" unless defined $file;

    my $env = {};
    return $env if $env = $self->_try_any_config_file($file);

    open my $fh, '<', $file or Carp::croak "$file: $!";
    while (defined (my $line = readline $fh)) {
        chomp $line;
        next if index($line, '#') == 0;
        next if $line =~ /^\s*$/;
        my ($key, $value) = $self->_parse_line($line);
        $env->{$key} = $value;
    }
    close $fh;

    return $env;
}

sub _try_any_config_file {
    my ($self, $file) = @_;

    my ($ext) = $file =~ /\.(\w+)/;
    if (my $type = $EXTENTIONS_MAP->{lc($ext || '')}) {
        my $env;
        if ($type eq 'Perl') {
            $env = do "$file";
            die $@ if $@;
        }
        else {
            require Data::Encoder;
            $env = Data::Encoder->load($type)->decode($self->_slurp($file));
        }
        die "$file: Should be return HASHREF\n" unless ref $env eq 'HASH';
        return $env;
    }

    return;
}

sub _slurp {
    my ($self, $file) = @_;
    my $data = do {
        local $\;
        open my $fh, '<', $file or die "$file: $!\n";
        <$fh>;
    };
    return $data;
}

sub _parse_line {
    my ($self, $line) = @_;
    my ($key, $value) = map { my $str = $_; $str =~ s/^\s+|\s+$//g; $str } split '=', $line, 2;
    return $key, $value;
}

1;
__END__

=encoding utf-8

=for stopwords

=head1 NAME

App::envfile - runs another program with environment modified according to envfile

=head1 SYNOPSIS

  $ cat > foo.env
  FOO=bar
  HOGE=fuga
  $ envfile foo.env perl -le 'print "$ENV{FOO}, $ENV{HOGE}"'
  bar, fuga

like

  $ env FOO=bar HOGE=fuga perl -le 'print "$ENV{FOO}, $ENV{HOGE}"'

=head1 DESCRIPTION

App::envfile is sets environment from file.

envfile inspired djb's envdir program.

=head1 METHODS

=head2 new()

Create App::envfile instance.

  my $envf = App::envfile->new();

=head2 run_with_env(\%env, \@commands)

Runs another program with environment modified according to C<< \%env >>.

  $envf->run_with_env(\%env, \@commands);

=head2 parse_envfile($envfile)

Parse the C<< envfile >>. Returned value is HASHREF.

  my $env = $envf->parse_envfile($envfile);

Supported file format are:

  KEY=VALUE
  # comment
  KEY2=VALUE
  ...

Or more supported C<< Perl >>, C<< JSON >> and C<< YAML >> format.
The file format is determined by the extension type. extensions map are:

  pl   => Perl
  perl => Perl
  js   => JSON
  json => JSON
  yml  => YAML
  yaml => YAML

If this list does not match then considers that file is envfile.

Also, if you use C<< YAML >> and C<< JSON >>, L<< Data::Encoder >> and L<< YAML >> or L<< JSON >> module is required.

=head1 AUTHOR

xaicron E<lt>xaicron@cpan.orgE<gt>

=head1 THANKS TO

tokuhirom

=head1 COPYRIGHT

Copyright 2011 - xaicron

=head1 LICENSE

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

=head1 SEE ALSO

=cut
