package Venus;

use 5.018;

use strict;
use warnings;

# VERSION

our $VERSION = '2.01';

# AUTHORITY

our $AUTHORITY = 'cpan:AWNCORP';

# IMPORTS

sub import {
  my ($self, @args) = @_;

  my $target = caller;

  no strict 'refs';

  my %exports = (
    cast => 1,
    catch => 1,
    caught => 1,
    error => 1,
    false => 1,
    fault => 1,
    raise => 1,
    true => 1,
  );

  @args = grep defined && !ref && /^[A-Za-z]/ && $exports{$_}, @args;

  my %seen;
  for my $name (grep !$seen{$_}++, @args, 'true', 'false') {
    *{"${target}::${name}"} = $self->can($name) if !$target->can($name);
  }

  return $self;
}

# FUNCTIONS

sub cast (;$$) {
  my ($data, $into) = (@_ ? (@_) : ($_));

  require Venus::Type;

  my $type = Venus::Type->new($data);

  return $into ? $type->cast($into) : $type->deduce;
}

sub catch (&) {
  my ($data) = @_;

  my $error;

  require Venus::Try;

  my @result = Venus::Try->new($data)->error(\$error)->result;

  return wantarray ? ($error ? ($error, undef) : ($error, @result)) : $error;
}

sub caught ($$;&) {
  my ($data, $type, $code) = @_;

  ($type, my($name)) = @$type if ref $type eq 'ARRAY';

  my $is_true = $data
    && UNIVERSAL::isa($data, $type)
    && UNIVERSAL::isa($data, 'Venus::Error')
    && (
      $data->name
        ? ($name ? $data->of($name) : true())
        : (!$name ? true() : false())
    );

  if ($is_true) {
    local $_ = $data;

    return $code ? $code->($data) : $data;
  }
  else {
    return undef;
  }
}

sub error (;$) {
  my ($data) = @_;

  $data //= {};
  $data->{context} //= (caller(1))[3];

  require Venus::Throw;

  return Venus::Throw->new->error($data);
}

sub false () {
  require Venus::False;

  return Venus::False->value;
}

sub fault (;$) {
  my ($data) = @_;

  require Venus::Fault;

  return Venus::Fault->new($data)->throw;
}

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

  ($self, my $parent) = (@$self) if (ref($self) eq 'ARRAY');

  $data //= {};
  $data->{context} //= (caller(1))[3];

  $parent = 'Venus::Error' if !$parent;

  require Venus::Throw;

  return Venus::Throw->new(package => $self, parent => $parent)->error($data);
}

sub true () {
  require Venus::True;

  return Venus::True->value;
}

1;


=head1 NAME

Venus - OO Library

=cut

=head1 ABSTRACT

OO Standard Library for Perl 5

=cut

=head1 VERSION

2.01

=cut

=head1 SYNOPSIS

  package main;

  use Venus qw(
    catch
    error
    raise
  );

  # error handling
  my ($error, $result) = catch {
    error;
  };

  # boolean keywords
  if ($result and $result eq false) {
    true;
  }

  # raise exceptions
  if (false) {
    raise 'MyApp::Error';
  }

  # and much more!
  true ne false;

=cut

=head1 DESCRIPTION

This library provides an object-orientation framework and extendible standard
library for Perl 5, built on top of the L<Mars> architecture with classes which
wrap most native Perl data types. Venus has a simple modular architecture,
robust library of classes, methods, and roles, supports pure-Perl autoboxing,
advanced exception handling, "true" and "false" functions, package
introspection, command-line options parsing, and more. This package will always
automatically exports C<true> and C<false> keyword functions (unless existing
routines of the same name already exist in the calling package or its parents),
otherwise exports keyword functions as requested at import. This library
requires Perl C<5.18+>.

=cut

=head1 FUNCTIONS

This package provides the following functions:

=cut

=head2 cast

  cast(Any $data, Str $type) (Object)

The cast function returns the argument provided as an object, promoting native
Perl data types to data type objects. The optional second argument can be the
name of the type for the object to cast to explicitly.

I<Since C<1.40>>

=over 4

=item cast example 1

  package main;

  use Venus 'cast';

  my $undef = cast;

  # bless({value => undef}, "Venus::Undef")

=back

=over 4

=item cast example 2

  package main;

  use Venus 'cast';

  my @booleans = map cast, true, false;

  # (bless({value => 1}, "Venus::Boolean"), bless({value => 0}, "Venus::Boolean"))

=back

=over 4

=item cast example 3

  package main;

  use Venus 'cast';

  my $example = cast bless({}, "Example");

  # bless({value => 1}, "Example")

=back

=over 4

=item cast example 4

  package main;

  use Venus 'cast';

  my $float = cast 1.23;

  # bless({value => "1.23"}, "Venus::Float")

=back

=cut

=head2 catch

  catch(CodeRef $block) (Error, Any)

The catch function executes the code block trapping errors and returning the
caught exception in scalar context, and also returning the result as a second
argument in list context.

I<Since C<0.01>>

=over 4

=item catch example 1

  package main;

  use Venus 'catch';

  my $error = catch {die};

  $error;

  # "Died at ..."

=back

=over 4

=item catch example 2

  package main;

  use Venus 'catch';

  my ($error, $result) = catch {error};

  $error;

  # bless({...}, 'Venus::Error')

=back

=over 4

=item catch example 3

  package main;

  use Venus 'catch';

  my ($error, $result) = catch {true};

  $result;

  # 1

=back

=cut

=head2 caught

  caught(Object $error, Str | Tuple[Str, Str] $identity, CodeRef $block) (Any)

The caught function evaluates the exception object provided and validates its
identity and name (if provided) then executes the code block provided returning
the result of the callback. If no callback is provided this function returns
the exception object on success and C<undef> on failure.

I<Since C<1.95>>

=over 4

=item caught example 1

  package main;

  use Venus 'catch', 'caught', 'error';

  my $error = catch { error };

  my $result = caught $error, 'Venus::Error';

  # bless(..., 'Venus::Error')

=back

=over 4

=item caught example 2

  package main;

  use Venus 'catch', 'caught', 'raise';

  my $error = catch { raise 'Example::Error' };

  my $result = caught $error, 'Venus::Error';

  # bless(..., 'Venus::Error')

=back

=over 4

=item caught example 3

  package main;

  use Venus 'catch', 'caught', 'raise';

  my $error = catch { raise 'Example::Error' };

  my $result = caught $error, 'Example::Error';

  # bless(..., 'Venus::Error')

=back

=over 4

=item caught example 4

  package main;

  use Venus 'catch', 'caught', 'raise';

  my $error = catch { raise 'Example::Error', { name => 'on.test' } };

  my $result = caught $error, ['Example::Error', 'on.test'];

  # bless(..., 'Venus::Error')

=back

=over 4

=item caught example 5

  package main;

  use Venus 'catch', 'caught', 'raise';

  my $error = catch { raise 'Example::Error', { name => 'on.recv' } };

  my $result = caught $error, ['Example::Error', 'on.send'];

  # undef

=back

=over 4

=item caught example 6

  package main;

  use Venus 'catch', 'caught', 'error';

  my $error = catch { error };

  my $result = caught $error, ['Example::Error', 'on.send'];

  # undef

=back

=over 4

=item caught example 7

  package main;

  use Venus 'catch', 'caught', 'error';

  my $error = catch { error };

  my $result = caught $error, ['Example::Error'];

  # undef

=back

=over 4

=item caught example 8

  package main;

  use Venus 'catch', 'caught', 'error';

  my $error = catch { error };

  my $result = caught $error, 'Example::Error';

  # undef

=back

=over 4

=item caught example 9

  package main;

  use Venus 'catch', 'caught', 'error';

  my $error = catch { error { name => 'on.send' } };

  my $result = caught $error, ['Venus::Error', 'on.send'];

  # bless(..., 'Venus::Error')

=back

=over 4

=item caught example 10

  package main;

  use Venus 'catch', 'caught', 'error';

  my $error = catch { error { name => 'on.send.open' } };

  my $result = caught $error, ['Venus::Error', 'on.send'], sub {
    $error->stash('caught', true) if $error->is('on.send.open');
    return $error;
  };

  # bless(..., 'Venus::Error')

=back

=cut

=head2 error

  error(Maybe[HashRef] $args) (Error)

The error function throws a L<Venus::Error> exception object using the
exception object arguments provided.

I<Since C<0.01>>

=over 4

=item error example 1

  package main;

  use Venus 'error';

  my $error = error;

  # bless({...}, 'Venus::Error')

=back

=over 4

=item error example 2

  package main;

  use Venus 'error';

  my $error = error {
    message => 'Something failed!',
  };

  # bless({message => 'Something failed!', ...}, 'Venus::Error')

=back

=cut

=head2 false

  false() (Bool)

The false function returns a falsy boolean value which is designed to be
practically indistinguishable from the conventional numerical C<0> value.

I<Since C<0.01>>

=over 4

=item false example 1

  package main;

  use Venus;

  my $false = false;

  # 0

=back

=over 4

=item false example 2

  package main;

  use Venus;

  my $true = !false;

  # 1

=back

=cut

=head2 fault

  fault(Str $args) (Fault)

The fault function throws a L<Venus::Fault> exception object and represents a
system failure, and isn't meant to be caught.

I<Since C<1.80>>

=over 4

=item fault example 1

  package main;

  use Venus 'fault';

  my $fault = fault;

  # bless({message => 'Exception!'}, 'Venus::Fault')

=back

=over 4

=item fault example 2

  package main;

  use Venus 'fault';

  my $fault = fault 'Something failed!';

  # bless({message => 'Something failed!'}, 'Venus::Fault')

=back

=cut

=head2 raise

  raise(Str $class | Tuple[Str, Str] $class, Maybe[HashRef] $args) (Error)

The raise function generates and throws a named exception object derived from
L<Venus::Error>, or provided base class, using the exception object arguments
provided.

I<Since C<0.01>>

=over 4

=item raise example 1

  package main;

  use Venus 'raise';

  my $error = raise 'MyApp::Error';

  # bless({...}, 'MyApp::Error')

=back

=over 4

=item raise example 2

  package main;

  use Venus 'raise';

  my $error = raise ['MyApp::Error', 'Venus::Error'];

  # bless({...}, 'MyApp::Error')

=back

=over 4

=item raise example 3

  package main;

  use Venus 'raise';

  my $error = raise ['MyApp::Error', 'Venus::Error'], {
    message => 'Something failed!',
  };

  # bless({message => 'Something failed!', ...}, 'MyApp::Error')

=back

=cut

=head2 true

  true() (Bool)

The true function returns a truthy boolean value which is designed to be
practically indistinguishable from the conventional numerical C<1> value.

I<Since C<0.01>>

=over 4

=item true example 1

  package main;

  use Venus;

  my $true = true;

  # 1

=back

=over 4

=item true example 2

  package main;

  use Venus;

  my $false = !true;

  # 0

=back

=cut

=head1 FEATURES

This package provides the following features:

=cut

=over 4

=item standard-library

This library provides a Perl object-oriented standard library with value
classes and consistently named methods.

B<example 1>

  package main;

  use Venus::Array;

  my $array = Venus::Array->new([1..4]);

  # $array->all(sub{ $_ > 0 });
  # $array->any(sub{ $_ > 0 });
  # $array->each(sub{ $_ > 0 });
  # $array->grep(sub{ $_ > 0 });
  # $array->map(sub{ $_ > 0 });
  # $array->none(sub{ $_ < 0 });
  # $array->one(sub{ $_ == 0 });
  # $array->random;

  use Venus::Hash;

  my $hash = Venus::Hash->new({1..8});

  # $hash->all(sub{ $_ > 0 });
  # $hash->any(sub{ $_ > 0 });
  # $hash->each(sub{ $_ > 0 });
  # $hash->grep(sub{ $_ > 0 });
  # $hash->map(sub{ $_ > 0 });
  # $hash->none(sub{ $_ < 0 });
  # $hash->one(sub{ $_ == 0 });
  # $hash->random;

  $array->count == $hash->count;

  # 1

=back

=over 4

=item value-classes

This library provides value classes which wrap native Perl data types and
provides methods for operating their values.

B<example 1>

  package main;

  use Venus::Array;

  my $array = Venus::Array->new;

  # bless({...}, 'Venus::Array')

B<example 2>

  package main;

  use Venus::Boolean;

  my $boolean = Venus::Boolean->new;

  # bless({...}, 'Venus::Boolean')

B<example 3>

  package main;

  use Venus::Code;

  my $code = Venus::Code->new;

  # bless({...}, 'Venus::Code')

B<example 4>

  package main;

  use Venus::Float;

  my $float = Venus::Float->new;

  # bless({...}, 'Venus::Float')

B<example 5>

  package main;

  use Venus::Hash;

  my $hash = Venus::Hash->new;

  # bless({...}, 'Venus::Hash')

B<example 6>

  package main;

  use Venus::Number;

  my $number = Venus::Number->new;

  # bless({...}, 'Venus::Number')

B<example 7>

  package main;

  use Venus::Regexp;

  my $regexp = Venus::Regexp->new;

  # bless({...}, 'Venus::Regexp')

B<example 8>

  package main;

  use Venus::Scalar;

  my $scalar = Venus::Scalar->new;

  # bless({...}, 'Venus::Scalar')

B<example 9>

  package main;

  use Venus::String;

  my $string = Venus::String->new;

  # bless({...}, 'Venus::String')

B<example 10>

  package main;

  use Venus::Undef;

  my $undef = Venus::Undef->new;

  # bless({...}, 'Venus::Undef')

=back

=over 4

=item builtin-autoboxing

This library provides opt-in pure Perl autoboxing allowing you to chain methods
calls across objects and values.

B<example 1>

  package main;

  use Venus::String;

  my $string = Venus::String->new('hello, world');

  $string->box->split(', ')->join(' ')->titlecase->unbox->get;

  # Hello World

=back

=over 4

=item utility-classes

This library provides serveral essential utility classes for performing common
programming tasks.

B<example 1>

  package main;

  use Venus::Args;

  my $args = Venus::Args->new;

  # bless({...}, 'Venus::Args')

B<example 2>

  package main;

  use Venus::Box;

  my $box = Venus::Box->new;

  # bless({...}, 'Venus::Box')

B<example 3>

  package main;

  use Venus::Data;

  my $docs = Venus::Data->new->docs;

  # bless({...}, 'Venus::Data')

B<example 4>

  package main;

  use Venus::Date;

  my $date = Venus::Date->new;

  # bless({...}, 'Venus::Date')

B<example 5>

  package main;

  use Venus::Error;

  my $error = Venus::Error->new;

  # bless({...}, 'Venus::Error')

B<example 6>

  package main;

  use Venus::Json;

  my $json = Venus::Json->new;

  # bless({...}, 'Venus::Json')

B<example 7>

  package main;

  use Venus::Name;

  my $name = Venus::Name->new;

  # bless({...}, 'Venus::Name')

B<example 8>

  package main;

  use Venus::Opts;

  my $opts = Venus::Opts->new;

  # bless({...}, 'Venus::Opts')

B<example 9>

  package main;

  use Venus::Path;

  my $path = Venus::Path->new;

  # bless({...}, 'Venus::Path')

B<example 10>

  package main;

  use Venus::Data;

  my $text = Venus::Data->new->text;

  # bless({...}, 'Venus::Data')

B<example 11>

  package main;

  use Venus::Space;

  my $space = Venus::Space->new;

  # bless({...}, 'Venus::Space')

B<example 12>

  package main;

  use Venus::Throw;

  my $throw = Venus::Throw->new;

  # bless({...}, 'Venus::Throw')

B<example 13>

  package main;

  use Venus::Try;

  my $try = Venus::Try->new;

  # bless({...}, 'Venus::Try')

B<example 14>

  package main;

  use Venus::Type;

  my $type = Venus::Type->new;

  # bless({...}, 'Venus::Type')

B<example 15>

  package main;

  use Venus::Vars;

  my $vars = Venus::Vars->new;

  # bless({...}, 'Venus::Vars')

B<example 16>

  package main;

  use Venus::Match;

  my $match = Venus::Match->new;

  # bless({...}, 'Venus::Match')

B<example 17>

  package main;

  use Venus::Process;

  my $process = Venus::Process->new;

  # bless({...}, 'Venus::Process')

B<example 18>

  package main;

  use Venus::Template;

  my $template = Venus::Template->new;

  # bless({...}, 'Venus::Template')

B<example 19>

  package main;

  use Venus::Yaml;

  my $yaml = Venus::Yaml->new;

  # bless({...}, 'Venus::Yaml')

=back

=over 4

=item package-reflection

This library provides a package reflection class, L<Venus::Space>, which can be
used to perform meta-programming on package spaces.

B<example 1>

  package main;

  use Venus::Space;

  my $space = Venus::Space->new('Venus');

  $space->do('tryload')->routines;

  # [...]

=back

=over 4

=item exception-handling

This library provides a framework for raising, i.e. generating and throwing,
exception objects and catching them.

B<example 1>

  package MyApp;

  use Venus::Class;

  with 'Venus::Role::Tryable';
  with 'Venus::Role::Throwable';
  with 'Venus::Role::Catchable';

  sub execute {
    my ($self) = @_;

    $self->throw->error;
  }

  package main;

  my $myapp = MyApp->new;

  my $error = $myapp->catch('execute');

  # bless({...}, 'MyApp::Error');

=back

=over 4

=item composable-standards

This library provides a library of composable roles which can be used to extend
core behaviors to custom objects.

B<example 1>

  package MyApp;

  use Venus::Class;

  with 'Venus::Role::Dumpable';
  with 'Venus::Role::Stashable';

  package main;

  my $myapp = MyApp->new;

  $myapp->stash(greeting => 'hello world');

  $myapp->dump('stash');

  # '{"greeting" => "hello world"}'

=back

=over 4

=item pluggable-library

This library provides a mechanism for extending the standard library, i.e.
value classes, using plugins which can be automatically discovered and invoked.
(no monkey-patching necessary)

B<example 1>

  package Venus::String::Plugin::Base64;

  sub new {
    return bless {};
  }

  sub execute {
    my ($self, $string, @args) = @_;

    require MIME::Base64;

    return MIME::Base64::encode_base64($string->value);
  }

  package main;

  use Venus::String;

  my $string = Venus::String->new('hello, world');

  $string->base64;

  # "aGVsbG8sIHdvcmxk\n"

=back

=over 4

=item template-system

This library provides a minimalistic templating system.

B<example 1>

  package main;

  use Venus::Template;

  my $template = Venus::Template->new(q(
    {{ if user.name }}
    Welcome, {{ user.name }}!
    {{ else user.name }}
    Welcome, friend!
    {{ end user.name }}
  ));

  $template->render;

  # "Welcome, friend!"

=back

=head1 AUTHORS

Awncorp, C<awncorp@cpan.org>

=cut

=head1 LICENSE

Copyright (C) 2000, Al Newkirk.

This program is free software, you can redistribute it and/or modify it under
the terms of the Apache license version 2.0.

=cut