package Venus::Fault;
use 5.018;
use strict;
use warnings;
use overload (
'""' => 'explain',
'eq' => sub{$_[0]->{message} eq "$_[1]"},
'ne' => sub{$_[0]->{message} ne "$_[1]"},
'qr' => sub{qr/@{[quotemeta($_[0]->{message})]}/},
'~~' => 'explain',
fallback => 1,
);
# METHODS
sub new {
return bless({message => $_[1] || 'Exception!'})->trace;
}
sub explain {
my ($self) = @_;
$self->trace(1) if !@{$self->frames};
my $frames = $self->{'$frames'};
my $file = $frames->[0][1];
my $line = $frames->[0][2];
my $pack = $frames->[0][0];
my $subr = $frames->[0][3];
my $message = $self->{message};
my @stacktrace = ("$message in $file at line $line");
push @stacktrace, 'Traceback (reverse chronological order):' if @$frames > 1;
@stacktrace = (join("\n\n", grep defined, @stacktrace), '');
for (my $i = 1; $i < @$frames; $i++) {
my $pack = $frames->[$i][0];
my $file = $frames->[$i][1];
my $line = $frames->[$i][2];
my $subr = $frames->[$i][3];
push @stacktrace, "$subr\n in $file at line $line";
}
return join "\n", @stacktrace, "";
}
sub frames {
my ($self) = @_;
return $self->{'$frames'} //= [];
}
sub throw {
my ($self, @args) = @_;
$self = $self->new(@args) if !ref $self;
die $self;
}
sub trace {
my ($self, $offset, $limit) = @_;
my $frames = $self->frames;
@$frames = ();
for (my $i = $offset // 1; my @caller = caller($i); $i++) {
push @$frames, [@caller];
last if defined $limit && $i + 1 == $offset + $limit;
}
return $self;
}
1;
=head1 NAME
Venus::Fault - Fault Class
=cut
=head1 ABSTRACT
Fault Class for Perl 5
=cut
=head1 SYNOPSIS
package main;
use Venus::Fault;
my $fault = Venus::Fault->new;
# $fault->throw;
=cut
=head1 DESCRIPTION
This package represents a generic system error (exception object).
=cut
=head1 METHODS
This package provides the following methods:
=cut
=head2 explain
explain() (Str)
The explain method returns the error message and is used in stringification
operations.
I<Since C<1.80>>
=over 4
=item explain example 1
# given: synopsis;
my $explain = $fault->explain;
# "Exception! in ...
=back
=cut
=head2 frames
frames() (ArrayRef)
The frames method returns the compiled and stashed stack trace data.
I<Since C<1.80>>
=over 4
=item frames example 1
# given: synopsis;
my $frames = $fault->frames;
# [
# ...
# [
# "main",
# "t/Venus_Fault.t",
# ...
# ],
# ]
=back
=cut
=head2 throw
throw(Str $message) (Fault)
The throw method throws an error if the invocant is an object, or creates an
error object using the arguments provided and throws the created object.
I<Since C<1.80>>
=over 4
=item throw example 1
# given: synopsis;
my $throw = $fault->throw;
# bless({ ... }, 'Venus::Fault')
=back
=cut
=head2 trace
trace(Int $offset, Int $limit) (Fault)
The trace method compiles a stack trace and returns the object. By default it
skips the first frame.
I<Since C<1.80>>
=over 4
=item trace example 1
# given: synopsis;
my $trace = $fault->trace;
# bless({ ... }, 'Venus::Fault')
=back
=over 4
=item trace example 2
# given: synopsis;
my $trace = $fault->trace(0, 1);
# bless({ ... }, 'Venus::Fault')
=back
=over 4
=item trace example 3
# given: synopsis;
my $trace = $fault->trace(0, 2);
# bless({ ... }, 'Venus::Fault')
=back
=cut
=head1 OPERATORS
This package overloads the following operators:
=cut
=over 4
=item operation: C<(eq)>
This package overloads the C<eq> operator.
B<example 1>
# given: synopsis;
my $result = $fault eq 'Exception!';
# 1
=back
=over 4
=item operation: C<(ne)>
This package overloads the C<ne> operator.
B<example 1>
# given: synopsis;
my $result = $fault ne 'exception!';
# 1
=back
=over 4
=item operation: C<(qr)>
This package overloads the C<qr> operator.
B<example 1>
# given: synopsis;
my $test = 'Exception!' =~ qr/$fault/;
# 1
=back
=over 4
=item operation: C<("")>
This package overloads the C<""> operator.
B<example 1>
# given: synopsis;
my $result = "$fault";
# "Exception!"
=back
=over 4
=item operation: C<(~~)>
This package overloads the C<~~> operator.
B<example 1>
# given: synopsis;
my $result = $fault ~~ 'Exception!';
# 1
=back