The Perl Toolchain Summit 2025 Needs You: You can help ๐Ÿ™ Learn more

package YATT::Lite::Partial::ErrorReporter; sub MY () {__PACKAGE__}
# -*- coding: utf-8 -*-
use strict;
use warnings qw(FATAL all NONFATAL misc);
(fields => [qw/cf_at_done
cf_error_handler
cf_die_in_error
cf_ext_pattern
cf_in_sig_die
/]);
use constant DEBUG_ERROR => $ENV{DEBUG_YATT_ERROR};
use YATT::Lite::Util qw/incr_opt/;
#========================================
# error reporting.
#========================================
sub error {
(my MY $self) = map {ref $_ ? $_ : MY} shift;
$self->raise(error => incr_opt(depth => \@_), @_);
}
sub error_with_status {
(my MY $self) = map {ref $_ ? $_ : MY} shift;
my ($code) = shift;
my $opts = incr_opt(depth => \@_);
$opts->{http_status_code} = $code;
$self->raise(error => $opts, @_);
}
sub make_error {
my ($self, $depth, $opts) = splice @_, 0, 3;
my ($fmtOrReason, @args) = @_;
my ($pkg, $file, $line) = caller($depth);
my $bt = do {
my @bt_opts = (ignore_package => [__PACKAGE__]);
if (my $frm = delete $opts->{ignore_frame}) {
# $YATT::Lite::CON->logdump(ignore_frame => $frm);
push @bt_opts, frame_filter => sub {
my ($hash) = @_;
my $caller = $hash->{'caller'};
my $all_match = grep {($frm->[$_] // '') eq ($caller->[$_] // '')}
1, 2; # __FILE__, __LINE__
# print STDERR YATT::Lite::Util::terse_dump("filter: ", $all_match, $frm, $caller), "\n";
$all_match != 2;
}
}
Devel::StackTrace->new(@bt_opts);
};
my $pattern = $self->{cf_ext_pattern} // qr/\.(yatt|ytmpl|ydo)$/;
my @tmplinfo;
foreach my $fr ($bt->frames) {
my $fn = $fr->filename
or next;
$fn =~ $pattern
or next;
push @tmplinfo, tmpl_file => $fn, tmpl_line => $fr->line;
last;
}
my @error_diag = do {
if (@args) {
# Errors from YATT can have arguments for sprintf.
(format => $fmtOrReason, args => \@args);
} else {
# (Possibly) errors from perl runtime itself. Should avoid use of sprintf.
(reason => do {
if (Encode::is_utf8($fmtOrReason) and not utf8::valid($fmtOrReason)) {
# Some errors from perl runtime could be trimmed to 32bytes and
# it can be malformed utf8.
YATT::Lite::Util::reencode_malformed_utf8($fmtOrReason);
} else {
$fmtOrReason;
}
});
}
};
$self->Error->new
(file => $opts->{file} // $file, line => $opts->{line} // $line
, @tmplinfo
, @error_diag
, backtrace => $bt
, $opts ? %$opts : ());
}
# $yatt->raise($errType => ?{opts}?, $errFmt, @fmtArgs)
sub raise {
(my MY $self, my $type) = splice @_, 0, 2;
my $opts = shift if @_ and ref $_[0] eq 'HASH';
# shift/splice ใ—ใชใ„ใฎใฏใ€ๅผ•ๆ•ฐใ‚’ stack trace ใซๆฎ‹ใ—ใŸใ„ใ‹ใ‚‰
my $depth = (delete($opts->{depth}) // 0);
my Error $err = $self->make_error(2 + $depth, $opts, @_); # 2==raise+make_error
if (ref $self and my $sub = deref($self->{cf_error_handler})) {
# $con ใ‚’ๅผ•ๆ•ฐใงๅผ•ใใšใ‚Šๅ›žใ™ใฎใฏๅคงๅค‰ใชใฎใงใ€ใ‚€ใ—ใ‚ๅค–ใ‹ใ‚‰ closure ใ‚’ๆธกใใ†ใ€ใจใ€‚
# $SIG{__DIE__} ใ‚’ไฝฟใ‚ใชใ„ใฎใฏใชใœใ‹ใฃใฆ? ใใ‚Œใฏใƒฆใƒผใ‚ถใซ้–‹ๆ”พใ—ใฆใŠใใŸใ„ใฎใ‚ˆใ‚“ใ€‚
print STDERR "# raise by cf_error_handler\n" if DEBUG_ERROR;
unless (ref $sub eq 'CODE') {
die "error_handler is not a CODE ref: $sub";
}
$sub->($type, $err);
} elsif ($sub = $self->can('error_handler')) {
print STDERR "# raise by ->error_handler\n" if DEBUG_ERROR;
$sub->($self, $type, $err);
} elsif (not ref $self or $self->{cf_die_in_error}) {
print STDERR "# raise by die_in_error\n" if DEBUG_ERROR;
die $err->message;
} elsif ($err->{cf_http_status_code}) {
print STDERR "# raise by http_status_code\n" if DEBUG_ERROR;
# If http_status_code is specified explicitly (from error_with_status),
# raise it immediately, with simple reason. (not full backtrace message).
$self->raise_psgi_html($err->{cf_http_status_code}
, $err->reason);
} else {
print STDERR "# raise pass-thrue error object\n" if DEBUG_ERROR;
# ๅณๅบงใซ die ใ—ใชใ„ใƒขใƒผใƒ‰ใฏใ€ใƒ‡ใƒใƒƒใ‚ฌใ‹ใ‚‰ error ๅ‘ผใณๅ‡บใ—็ฎ‡ๆ‰€ใซ step ใ—ใฆๆˆปใ‚Œใ‚‹ใ‚ˆใ†ใซใ™ใ‚‹ใŸใ‚ใ€‚
# ... ใงใ‚‚ใ€ๅ—ใ‘ๅดใ‚’ do {my $err = $con->error; die $err} ใซใงใ‚‚ใ—ใชใใ‚ƒใƒ€ใƒกใ‹ใ‚‚?
return $err;
}
}
# XXX: ๅฐ†ๆฅใ€ๆ‹กๅผตใ•ใ‚Œใ‚‹ใ‹ใ‚‚ใ€‚
sub DONE {
my MY $self = shift;
if (my $sub = $self->{cf_at_done}) {
$sub->(@_);
} else {
die \ 'DONE';
}
}
sub raise_psgi_html {
(my MY $self, my ($status, $html, @rest)) = @_;
die [$status, ["Content-type" => "text/html; charset=utf-8", @rest]
, [$html]];
}
sub deref {
return undef unless defined $_[0];
if (ref $_[0] eq 'REF' or ref $_[0] eq 'SCALAR') {
${$_[0]};
} else {
$_[0];
}
}
1;