]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/SearchThread.pm
treewide: replace {-inbox} with {ibx} for consistency
[public-inbox.git] / lib / PublicInbox / SearchThread.pm
index 8b2cb8059db94043affbbf82abbfbea2eddb57ba..8fb3a030aa723417036108bb1728d8abeec8be4f 100644 (file)
@@ -24,44 +24,38 @@ use PublicInbox::MID qw($MID_EXTRACT);
 
 sub thread {
        my ($msgs, $ordersub, $ctx) = @_;
-       my $id_table = {};
+
+       # A. put all current $msgs (non-ghosts) into %id_table
+       my %id_table = map {;
+               # this delete saves around 4K across 1K messages
+               # TODO: move this to a more appropriate place, breaks tests
+               # if we do it during psgi_cull
+               delete $_->{num};
+
+               $_->{mid} => PublicInbox::SearchThread::Msg::cast($_);
+       } @$msgs;
 
        # Sadly, we sort here anyways since the fill-in-the-blanks References:
        # can be shakier if somebody used In-Reply-To with multiple, disparate
        # messages.  So, take the client Date: into account since we can't
-       # alway determine ordering when somebody uses multiple In-Reply-To.
+       # always determine ordering when somebody uses multiple In-Reply-To.
        # We'll trust the client Date: header here instead of the Received:
        # time since this is for display (and not retrieval)
-       _add_message($id_table, $_) for sort { $a->{ds} <=> $b->{ds} } @$msgs;
-       my $ibx = $ctx->{-inbox};
+       _set_parent(\%id_table, $_) for sort { $a->{ds} <=> $b->{ds} } @$msgs;
+       my $ibx = $ctx->{ibx};
        my $rootset = [ grep {
                        !delete($_->{parent}) && $_->visible($ibx)
-               } values %$id_table ];
-       $id_table = undef;
+               } values %id_table ];
        $rootset = $ordersub->($rootset);
        $_->order_children($ordersub, $ctx) for @$rootset;
        $rootset;
 }
 
-sub _get_cont_for_id ($$) {
-       my ($id_table, $mid) = @_;
-       $id_table->{$mid} ||= PublicInbox::SearchThread::Msg->new($mid);
-}
-
-sub _add_message ($$) {
-       my ($id_table, $smsg) = @_;
-
-       # A. if id_table...
-       my $this = _get_cont_for_id($id_table, $smsg->{mid});
-       $this->{smsg} = $smsg;
-
-       # saves around 4K across 1K messages
-       # TODO: move this to a more appropriate place, breaks tests
-       # if we do it during psgi_cull
-       delete $smsg->{num};
+sub _set_parent ($$) {
+       my ($id_table, $this) = @_;
 
        # B. For each element in the message's References field:
-       defined(my $refs = $smsg->{references}) or return;
+       defined(my $refs = $this->{references}) or return;
 
        # This loop exists to help fill in gaps left from missing
        # messages.  It is not needed in a perfect world where
@@ -70,7 +64,8 @@ sub _add_message ($$) {
        my $prev;
        foreach my $ref ($refs =~ m/$MID_EXTRACT/go) {
                # Find a Container object for the given Message-ID
-               my $cont = _get_cont_for_id($id_table, $ref);
+               my $cont = $id_table->{$ref} //=
+                       PublicInbox::SearchThread::Msg::ghost($ref);
 
                # Link the References field's Containers together in
                # the order implied by the References header
@@ -96,22 +91,31 @@ sub _add_message ($$) {
 }
 
 package PublicInbox::SearchThread::Msg;
+use base qw(PublicInbox::Smsg);
 use strict;
 use warnings;
 use Carp qw(croak);
 
-sub new {
+# declare a ghost smsg (determined by absence of {blob})
+sub ghost {
        bless {
-               id => $_[1],
+               mid => $_[0],
                children => {}, # becomes an array when sorted by ->order(...)
-       }, $_[0];
+       }, __PACKAGE__;
+}
+
+# give a existing smsg the methods of this class
+sub cast {
+       my ($smsg) = @_;
+       $smsg->{children} = {};
+       bless $smsg, __PACKAGE__;
 }
 
 sub topmost {
        my ($self) = @_;
        my @q = ($self);
        while (my $cont = shift @q) {
-               return $cont if $cont->{smsg};
+               return $cont if $cont->{blob};
                push @q, values %{$cont->{children}};
        }
        undef;
@@ -122,7 +126,7 @@ sub add_child {
        croak "Cowardly refusing to become my own parent: $self"
          if $self == $child;
 
-       my $cid = $child->{id};
+       my $cid = $child->{mid};
 
        # reparenting:
        if (defined(my $parent = $child->{parent})) {
@@ -148,8 +152,13 @@ sub has_descendent {
 # being folded/mangled by a MUA, and not a missing message.
 sub visible ($$) {
        my ($self, $ibx) = @_;
-       ($self->{smsg} ||= eval { $ibx->smsg_by_mid($self->{id}) }) ||
-        (scalar values %{$self->{children}});
+       return 1 if $self->{blob};
+       if (my $by_mid = $ibx->smsg_by_mid($self->{mid})) {
+               %$self = (%$self, %$by_mid);
+               1;
+       } else {
+               (scalar values %{$self->{children}});
+       }
 }
 
 sub order_children {
@@ -157,7 +166,7 @@ sub order_children {
 
        my %seen = ($cur => 1); # self-referential loop prevention
        my @q = ($cur);
-       my $ibx = $ctx->{-inbox};
+       my $ibx = $ctx->{ibx};
        while (defined($cur = shift @q)) {
                my $c = $cur->{children}; # The hashref here...