<commit_before>package Sisimai::Reason;
use feature ':5.10';
use strict;
use warnings;
use Module::Load '';

sub retry { 
    # Reason list better to retry detecting an error reason
    # @return   [Array] Reason list
    return [ 
        'undefined', 'onhold', 'systemerror', 'securityerror', 'networkerror'
    ]
}

sub index {
    # All the error reason list Sisimai support
    # @return   [Array] Reason list
    return [ qw|
        Blocked ContentError ExceedLimit Expired Filtered HasMoved HostUnknown
        MailboxFull MailerError MesgTooBig NetworkError NotAccept OnHold 
        Rejected NoRelaying SpamDetected SecurityError Suspend SystemError
        SystemFull TooManyConn UserUnknown
    | ];
}

sub get {
    # Detect the bounce reason
    # @param    [Sisimai::Data] argvs   Parsed email object
    # @return   [String, Undef]         Bounce reason or Undef if the argument
    #                                   is missing or invalid object
    # @see anotherone
    my $class = shift;
    my $argvs = shift // return undef;

    return undef unless ref $argvs eq 'Sisimai::Data';

    unless( grep { $argvs->reason eq $_ } @{ __PACKAGE__->retry } ) {
        # Return reason text already decided except reason match with the 
        # regular expression of ->retry() method.
        return $argvs->reason if length $argvs->reason;
    }

    my $reasontext = '';
    my $classorder = [
        'MailboxFull', 'MesgTooBig', 'ExceedLimit', 'Suspend', 'HasMoved', 
        'NoRelaying', 'UserUnknown', 'Filtered', 'Rejected', 'HostUnknown',
        'SpamDetected', 'TooManyConn', 'Blocked',
    ];

    if( $argvs->diagnostictype eq 'SMTP' || $argvs->diagnostictype eq '' ) {
        # Diagnostic-Code: SMTP; ... or empty value
        for my $e ( @$classorder ) {
            # Check the value of Diagnostic-Code: and the value of Status:, it is a
            # deliverystats, with true() method in each Sisimai::Reason::* class.
            my $p = 'Sisimai::Reason::'.$e;
            Module::Load::load( $p );

            next unless $p->true( $argvs );
            $reasontext = $p->text;
            last;
        }
    }

    if( not $reasontext ) {
        # Bounce reason is not detected yet.
        while( 1 ) {
            # Check with other patterns
            my $p = '';

            # Onhold ?
            $p = 'Sisimai::Reason::OnHold';
            Module::Load::load( $p );
            $reasontext = $p->text if $p->true( $argvs );
            last if $reasontext;

            # Other reason ?
            $reasontext = __PACKAGE__->anotherone( $argvs );
            last;
        }

        $reasontext ||= 'expired' if $argvs->action eq 'delayed';
        $reasontext ||= 'onhold'  if length $argvs->diagnosticcode;
        $reasontext ||= 'undefined';
    }

    return $reasontext;
}

sub anotherone {
    # Detect the other bounce reason, fall back method for get()
    # @param    [Sisimai::Data] argvs   Parsed email object
    # @return   [String, Undef]         Bounce reason or Undef if the argument
    #                                   is missing or invalid object
    # @see get
    my $class = shift;
    my $argvs = shift // return undef;

    return undef unless ref $argvs eq 'Sisimai::Data';
    return $argvs->reason if $argvs->reason;

    my $statuscode = $argvs->deliverystatus // '';
    my $diagnostic = $argvs->diagnosticcode // '';
    my $commandtxt = $argvs->smtpcommand    // '';
    my $reasontext = '';
    my $classorder = [
        'MailboxFull', 'SpamDetected', 'SecurityError', 'SystemError', 
        'NetworkError', 'Suspend', 'Expired', 'ContentError',
        'SystemFull', 'NotAccept', 'MailerError',
    ];
    my $retryingto = __PACKAGE__->retry;
    push @$retryingto, 'userunknown';

    require Sisimai::RFC3463;
    for my $e ( 'temporary', 'permanent' ) {
        $reasontext = Sisimai::RFC3463->reason( $statuscode, $e );
        last if $reasontext;
    }

    if( $reasontext eq '' || grep { $reasontext eq $_ } @$retryingto ) {
        # Could not decide the reason by the value of Status:
        for my $e ( @$classorder ) {
            # Trying to match with other patterns in Sisimai::Reason::* classes
            my $p = 'Sisimai::Reason::'.$e;
            Module::Load::load( $p );

            next unless $p->match( $diagnostic );
            $reasontext = lc $e;
            last;
        }

        if( not $reasontext ) {
            # Check the value of Status:
            my $v = substr( $statuscode, 0, 3 );
            if( $v eq '5.6' || $v eq '4.6' ) {<commit_msg>Define $RetryReasons for holding the result of retry() method: issue #115<commit_after>package Sisimai::Reason;
use feature ':5.10';
use strict;
use warnings;
use Module::Load '';

my $RetryReasons = __PACKAGE__->retry;

sub retry { 
    # Reason list better to retry detecting an error reason
    # @return   [Array] Reason list
    return [ 'undefined', 'onhold', 'systemerror', 'securityerror', 'networkerror' ];
}

sub index {
    # All the error reason list Sisimai support
    # @return   [Array] Reason list
    return [ qw|
        Blocked ContentError ExceedLimit Expired Filtered HasMoved HostUnknown
        MailboxFull MailerError MesgTooBig NetworkError NotAccept OnHold 
        Rejected NoRelaying SpamDetected SecurityError Suspend SystemError
        SystemFull TooManyConn UserUnknown
    | ];
}

sub get {
    # Detect the bounce reason
    # @param    [Sisimai::Data] argvs   Parsed email object
    # @return   [String, Undef]         Bounce reason or Undef if the argument
    #                                   is missing or invalid object
    # @see anotherone
    my $class = shift;
    my $argvs = shift // return undef;

    return undef unless ref $argvs eq 'Sisimai::Data';

    unless( grep { $argvs->reason eq $_ } @$RetryReasons ) {
        # Return reason text already decided except reason match with the 
        # regular expression of ->retry() method.
        return $argvs->reason if length $argvs->reason;
    }

    my $reasontext = '';
    my $classorder = [
        'MailboxFull', 'MesgTooBig', 'ExceedLimit', 'Suspend', 'HasMoved', 
        'NoRelaying', 'UserUnknown', 'Filtered', 'Rejected', 'HostUnknown',
        'SpamDetected', 'TooManyConn', 'Blocked',
    ];

    if( $argvs->diagnostictype eq 'SMTP' || $argvs->diagnostictype eq '' ) {
        # Diagnostic-Code: SMTP; ... or empty value
        for my $e ( @$classorder ) {
            # Check the value of Diagnostic-Code: and the value of Status:, it is a
            # deliverystats, with true() method in each Sisimai::Reason::* class.
            my $p = 'Sisimai::Reason::'.$e;
            Module::Load::load( $p );

            next unless $p->true( $argvs );
            $reasontext = $p->text;
            last;
        }
    }

    if( not $reasontext ) {
        # Bounce reason is not detected yet.
        while( 1 ) {
            # Check with other patterns
            my $p = '';

            # Onhold ?
            $p = 'Sisimai::Reason::OnHold';
            Module::Load::load( $p );
            $reasontext = $p->text if $p->true( $argvs );
            last if $reasontext;

            # Other reason ?
            $reasontext = __PACKAGE__->anotherone( $argvs );
            last;
        }

        $reasontext ||= 'expired' if $argvs->action eq 'delayed';
        $reasontext ||= 'onhold'  if length $argvs->diagnosticcode;
        $reasontext ||= 'undefined';
    }

    return $reasontext;
}

sub anotherone {
    # Detect the other bounce reason, fall back method for get()
    # @param    [Sisimai::Data] argvs   Parsed email object
    # @return   [String, Undef]         Bounce reason or Undef if the argument
    #                                   is missing or invalid object
    # @see get
    my $class = shift;
    my $argvs = shift // return undef;

    return undef unless ref $argvs eq 'Sisimai::Data';
    return $argvs->reason if $argvs->reason;

    my $statuscode = $argvs->deliverystatus // '';
    my $diagnostic = $argvs->diagnosticcode // '';
    my $commandtxt = $argvs->smtpcommand    // '';
    my $reasontext = '';
    my $classorder = [
        'MailboxFull', 'SpamDetected', 'SecurityError', 'SystemError', 
        'NetworkError', 'Suspend', 'Expired', 'ContentError',
        'SystemFull', 'NotAccept', 'MailerError',
    ];

    require Sisimai::RFC3463;
    for my $e ( 'temporary', 'permanent' ) {
        $reasontext = Sisimai::RFC3463->reason( $statuscode, $e );
        last if $reasontext;
    }

    if( $reasontext eq '' || $reasontext eq 'userunknown' ||
        grep { $reasontext eq $_ } @$RetryReasons ) {
        # Could not decide the reason by the value of Status:
        for my $e ( @$classorder ) {
            # Trying to match with other patterns in Sisimai::Reason::* classes
            my $p = 'Sisimai::Reason::'.$e;
            Module::Load::load( $p );

            next unless $p->match( $diagnostic );
            $reasontext = lc $e;
            last;
        }

        if( not $reasontext ) {
            # Check the value of Status:
            my $v = substr( $statuscode, 0, 3 );
            if( $v eq '5.6' || $v eq '4.6' ) {
{'fraction_non_alphanumeric': 0.1241844280121792, 'fraction_numerical': 0.004240974336668117, 'mean_word_length': 2.1572262272571234, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 46, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6305048', 'n_tokens_mistral': 2857, 'n_tokens_neox': 2750, 'n_words': 842}