#!/usr/bin/perl package Mail::Summary::Tools::ThreadFilter::Util; use strict; use warnings; use Sub::Exporter -setup => { exports => [qw/ get_root_message guess_mailing_list thread_root last_in_thread any_in_thread all_in_thread negate mailing_list_is in_date_range /], }; use Mail::ListDetector; use Date::Range; use DateTime::Format::Mail; { package Date::Range::Forgiving; use base qw/Date::Range/; sub want_class { "UNIVERSAL" } } use Scalar::Util qw/reftype blessed/; sub get_root_message ($) { my $thread = shift; my $message = $thread->message; $message = ($thread->threadMessages)[0] if !$message or $message->isDummy; die "Couldn't determine thread root!" if !$message or $message->isDummy; return $message; } sub negate ($) { my $filter = shift; return sub { not( $filter->( @_ ) ) }; } sub thread_root ($) { my $filter = shift; return sub { my $thread = shift; $filter->( get_root_message( $thread ) ); } } sub last_in_thread ($) { my $filter = shift; return sub { my $thread = shift; my $last = ($thread->threadMessages)[-1]; $filter->( $last ); } } sub any_in_thread ($) { my $filter = shift; return sub { my $thread = shift; my $match; $thread->recurse(sub { my $message = shift->message; return 1 if $message->isDummy; if ( $filter->( $message ) ) { $match = 1; return 0; # short circuit the recursion } else { return 1; } }); return $match; } } sub all_in_thread ($) { my $filter = shift; return sub { my $thread = shift; my $match = 1; $thread->recurse(sub { my $message = shift->message; return 1 if $message->isDummy; unless ( $filter->( $message ) ) { $match = 0; return 0; # short circuit the recursion } else { return 1; } }); return $match; } } sub guess_mailing_list ($) { my $message = shift; Mail::ListDetector->new( $message ); } sub mailing_list_is ($) { my $matchsub = _munge_list_match(shift); return sub { my $message = shift; my $list = guess_mailing_list( $message ); $list && $matchsub->( $list ); } } sub _munge_list_match { my $match = shift; if ( blessed($match) ) { return sub { $match->match( shift ) } } elsif ( ref($match) && reftype($match) eq "CODE" ) { return $match } else { return sub { no warnings 'uninitialized'; shift->listname eq $match } } } sub in_date_range ($$) { my $range = Date::Range::Forgiving->new( @_ ); return sub { my $message = shift; my $date_header = $message->head->get('Date')->unfoldedBody; my $date; $date_header =~ s/\s*<\S+@\S+>\s*$//; # seen numerous times my @errors; $date = eval { DateTime::Format::Mail->new->loose->parse_datetime( $date_header ) }; push @errors, $@ if $@; $date ||= eval { DateTime::Format::DateManip->parse_datetime( $date_header ) }; push @errors, $@ if $@; die "Error parsing date '$date_header': @errors" unless defined $date; return $range->includes( $date ); } } __PACKAGE__; __END__ =pod =head1 NAME Mail::Summary::Tools::ThreadFilter::Util - =head1 SYNOPSIS use Mail::Summary::Tools::ThreadFilter::Util; =head1 DESCRIPTION =cut