package Evo::Try;
use Evo '-Export *', '-Lib *';
use Carp 'croak';

export qw(try catch finally try_catch);

our @EXPORT = qw(try catch finally try_catch);
our %EXPORT_TAGS = (all => \@EXPORT);

use constant USAGE_ERR => <<HERE_ERR;
expected 2 functions at least, order matters:
  try {...} catch {...} finally {...}
  try_catch(sub {... }, sub {... }, sub {... });
HERE_ERR

use constant CATCH => 0b010;    # 1 is too popular
use constant FIN   => 0b100;

# if fin exists, it is always executed and returns result immidiately.
sub try_catch {
  my ($try, $catch, $fin) = @_;
  croak USAGE_ERR unless $try && ($catch || $fin);

  if ($fin) {
    local $@;
    eval { $try->(@_) };
    if (($@ || ref $@) && $catch) {
      my $err = $@;
      eval { $catch->($err) };
    }
    return $fin->() if !($@ || ref $@);
    $fin->();
    die $@;
  }
  else {
    my ($res, $err) = eval_context_run wantarray, $try;
    ($res, $err) = eval_context_run wantarray, $err, $catch if !$res && $catch;
    return context_result($res) if $res;
    die $err;
  }

}

sub try (&;@) {
  my $flag = pop;
  try_catch(shift, $flag & CATCH && shift, $flag & FIN && shift);
}

sub catch (&;@) {
  return (@_, CATCH) if @_ == 1;

  # only fin allowed after catch, parentheses for readability only
  croak USAGE_ERR if ($_[-1] | FIN) ^ FIN;
  $_[-1] |= CATCH;
  @_;
}

sub finally (&;@) { @_ == 1 ? (@_, FIN) : croak USAGE_ERR; }

__END__

=pod

=encoding UTF-8

=head1 NAME

Evo::Try

=head1 VERSION

version 0.0172

=head1 AUTHOR

alexbyk.com

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by alexbyk.

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
