#!/usr/bin/env perl
## no critic (ControlStructures::ProhibitPostfixControls)
## no critic (ValuesAndExpressions::ProhibitConstantPragma)
use strict;
use warnings;
use open ':std', IO => ':encoding(UTF-8)';

# ABSTRACT: Read .env file and turn its content into environment variables for different shells.

# VERSION: generated by DZP::OurPkgVersion

use English qw( -no_match_vars ); # Avoids regex performance penalty in perl 5.18 and earlier
use Getopt::Long;
use Carp;
use Pod::Usage;

use Env::Dot::Functions qw( get_dotenv_vars interpret_dotenv_filepath_var );

local $OUTPUT_AUTOFLUSH = 1;

use constant {
    DEFAULT_OPTION_DOTENV_FILENAME => '.env',
    DEFAULT_OPTION_SHELL => q{sh},
};

my $help = 0;
my $man = 0;
my $export = 1;
my $shell = DEFAULT_OPTION_SHELL;
my $dotenv_filepath = DEFAULT_OPTION_DOTENV_FILENAME;
GetOptions(
    'help|?'     => \$help,
    'man'        => \$man,
    'export!'    => \$export,
    'shell|s=s'  => \$shell,
    'dotenv|e=s' => \$dotenv_filepath,
) or pod2usage(2);
pod2usage(1) if $help;
pod2usage(-exitval => 0, -verbose => 2) if $man;

sub main {

    my $dotenv_filepath_var = q{DOTENV_FILEPATHS};
    my @dotenv_filepaths;
    if( exists $ENV{ $dotenv_filepath_var } ) {
        @dotenv_filepaths = interpret_dotenv_filepath_var( $ENV{ $dotenv_filepath_var } );
    } else {
        if( ! -f $dotenv_filepath ) {
            carp "No file found: '$dotenv_filepath'";
            return 1;
        }
        @dotenv_filepaths = ( $dotenv_filepath ); # The CLI parameter
    }

    my %vars = get_dotenv_vars( @dotenv_filepaths );

    print {*STDOUT} convert_variables_into_commands( %vars )
        or croak 'Cannot print variables to STDOUT';

    return 0;
}

=head2 convert_variables_into_commands( \%vars, $shell )

Convert variables for different shells: sh, csh, fish

Return a string.

=cut

sub convert_variables_into_commands {
    my ( %vars ) = @_;
    my $out = q{};
    foreach my $var_name ( sort keys %vars ) {
        my $var_value = $vars{$var_name};
        $out .= convert_variable( $var_name, $var_value );
    }
    return $out;
}

=head2 convert_variable( $name, $value )

Print one environmental variable definition according
to the wanted shell.

Return a string.

=cut

sub convert_variable {
    my ( $name, $value ) = @_;
    my $line;
    if( $shell eq q{sh} ) {
        if( $export ) {
            $line = sprintf "%s='%s'; export %s\n", $name, $value, $name;
        } else {
            $line = sprintf "%s='%s'\n", $name, $value;
        }
    } elsif( $shell eq q{csh} ) {
        if( $export ) {
            $line = sprintf "setenv %s '%s'\n", $name, $value;
        } else {
            $line = sprintf "set %s '%s'\n", $name, $value;
        }
    } elsif( $shell eq q{fish} ) {
        $line = sprintf "set -e %s; set -x -U %s '%s'\n", $name, $name, $value;
    } else {
        croak "Unknown shell: $shell";
    }
    return $line;
}

exit main(@ARGV);

__END__

=head1 envdot

Read .env file and turn its content into environment variables for different shells.



=head1 SYNOPSIS

envdot [options]

    eval `envdot`

    eval `envdot --no-export --shell csh --dotenv subdir/.env`

    ENVDOT_FILEPATHS=../.env:subdir/.env:.env eval `envdot`

Options:
    --help
    --man
    --export --no-export
    --shell -s
    --dotenv -e

=head2 CLI interface without dependencies

The F<envdot> command is also available
as a self contained executable.
You can download it and run it as it is without
additional installation of CPAN packages.
Of course, you still need Perl, but Perl comes with any
normal Linux installation.

This can be convenient if you want to, for instance,
include F<envdot> in a docker container build.

    curl -LSs -o envdot https://raw.githubusercontent.com/mikkoi/env-dot/master/script/envdot.packed
    chmod +x ./envdot


=head1 OPTIONS

=over 8

=item B<--help>

Print a brief help message and exits.

=item B<--man>

Prints the manual page and exits.

=item B<--export>, B<--no-export>

Write commands to set variables for local shell or for exporting them.
You usually want to export the variables
to all subsequent programs and subshells, i.e.
make them into I<environment variables>.

Default: export

=item B<-s>, B<--shell>

Which shell (family) are you using? Supported: sh, csh, fish.

Default: sh

=item B<-e>, B<--dotenv>

Path to F<.env> file.

Default: current directory F<.env>

=back

=head1 DESCRIPTION

B<envdot> reads your F<.env> file and converts it
into environment variable commands suitable for
different shells (shell families): B<sh>, B<csh> and B<fish>.

F<.env> files can be written in different flavors.
B<envdot> supports the often used B<sh> compatible flavor and
the B<docker> flavor which are not compatible with each other.

If you have several F<.env> files, you can read them in at one go
with the help of the environment variable B<ENVDOT_FILEPATHS>.
Separate the full paths with B<:> character.

If you have set the variable DOTENV_FILEPATH, then B<envdot> will use that.
Otherwise, it respects the command line parameter.
If no parameter, then default value is used. Default is the file
F<.env> in the current directory.

=head1 DEPENDENCIES

No external dependencies outside Perl's standard distribution.

=head1 SEE ALSO

L<Env::Assert> will verify that you certainly have those environmental
variables you need. It also has an executable which can perform the check
in the beginning of a B<docker> container run.

L<DotEnv> is another package which implements functionality to use
F<.env> files in Perl.

=cut
