about summary refs log tree commit homepage
path: root/lib
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2019-12-25 07:51:03 +0000
committerEric Wong <e@80x24.org>2019-12-28 19:38:10 +0000
commitb316f7541ec263fd04ea50530a1d500f43773299 (patch)
treed78a59ef1cd86a61481b91dba3020b41e8365688 /lib
parentbc23e3dc4b252dc7e9b6f3824afe74d85456b689 (diff)
downloadpublic-inbox-b316f7541ec263fd04ea50530a1d500f43773299.tar.gz
This allows us to get rid of the requirement to capture
on-stack variables with an anonymous sub, as illustrated
with the update to viewvcs to take advantage of this.

v2: fix error handling for missing OIDs
Diffstat (limited to 'lib')
-rw-r--r--lib/PublicInbox/SolverGit.pm38
-rw-r--r--lib/PublicInbox/ViewVCS.pm19
2 files changed, 31 insertions, 26 deletions
diff --git a/lib/PublicInbox/SolverGit.pm b/lib/PublicInbox/SolverGit.pm
index 9266fb08..17a43060 100644
--- a/lib/PublicInbox/SolverGit.pm
+++ b/lib/PublicInbox/SolverGit.pm
@@ -55,11 +55,16 @@ sub dbg ($$) {
         print { $_[0]->{out} } $_[1], "\n" or ERR($_[0], "print(dbg): $!");
 }
 
+sub done ($$) {
+        my ($self, $res) = @_;
+        my $ucb = delete($self->{user_cb}) or return;
+        $ucb->($res, $self->{uarg});
+}
+
 sub ERR ($$) {
         my ($self, $err) = @_;
         print { $self->{out} } $err, "\n";
-        my $ucb = delete($self->{user_cb});
-        eval { $ucb->($err) } if $ucb;
+        eval { done($self, $err) };
         die $err;
 }
 
@@ -311,24 +316,23 @@ sub extract_old_mode ($) {
         '100644';
 }
 
-sub do_finish ($$) {
-        my ($self, $user_cb) = @_;
-        my $found = $self->{found};
-        my $oid_want = $self->{oid_want};
+sub do_finish ($) {
+        my ($self) = @_;
+        my ($found, $oid_want) = @$self{qw(found oid_want)};
         if (my $exists = $found->{$oid_want}) {
-                return $user_cb->($exists);
+                return done($self, $exists);
         }
 
         # let git disambiguate if oid_want was too short,
         # but long enough to be unambiguous:
         my $tmp_git = $self->{tmp_git};
         if (my @res = $tmp_git->check($oid_want)) {
-                return $user_cb->($found->{$res[0]});
+                return done($self, $found->{$res[0]});
         }
         if (my $err = $tmp_git->last_check_err) {
                 dbg($self, $err);
         }
-        $user_cb->(undef);
+        done($self, undef);
 }
 
 sub event_step ($) {
@@ -352,8 +356,8 @@ sub event_step ($) {
                 # our result: (which may be undef)
                 # Other steps may call user_cb to terminate prematurely
                 # on error
-                } elsif (my $user_cb = delete($self->{user_cb})) {
-                        do_finish($self, $user_cb);
+                } elsif (exists $self->{user_cb}) {
+                        do_finish($self);
                 } else {
                         die 'about to call user_cb twice'; # Oops :x
                 }
@@ -362,8 +366,7 @@ sub event_step ($) {
         if ($err) {
                 $err =~ s/^\s*Exception:\s*//; # bad word to show users :P
                 dbg($self, "E: $err");
-                my $ucb = delete($self->{user_cb});
-                eval { $ucb->($err) } if $ucb;
+                eval { done($self, $err) };
         }
 }
 
@@ -524,7 +527,7 @@ sub resolve_patch ($$) {
                         join("\n", $found_git->pub_urls($self->{psgi_env})));
 
                 if ($cur_want eq $self->{oid_want} || $type ne 'blob') {
-                        eval { delete($self->{user_cb})->($existing) };
+                        eval { done($self, $existing) };
                         die "E: $@" if $@;
                         return;
                 }
@@ -562,18 +565,19 @@ sub resolve_patch ($$) {
         }
 
         dbg($self, "could not find $cur_want");
-        eval { delete($self->{user_cb})->(undef) }; # not found! :<
+        eval { done($self, undef) };
         die "E: $@" if $@;
 }
 
 # this API is designed to avoid creating self-referential structures;
 # so user_cb never references the SolverGit object
 sub new {
-        my ($class, $ibx, $user_cb) = @_;
+        my ($class, $ibx, $user_cb, $uarg) = @_;
 
         bless {
                 gits => $ibx->{-repo_objs},
                 user_cb => $user_cb,
+                uarg => $uarg,
                 # -cur_di, -qsp, -msg => temporary fields for Qspawn callbacks
 
                 # TODO: config option for searching related inboxes
@@ -591,7 +595,7 @@ sub solve ($$$$$) {
 
         # should we even get here? Probably not, but somebody
         # could be manually typing URLs:
-        return (delete $self->{user_cb})->(undef) if $oid_want =~ /\A0+\z/;
+        return done($self, undef) if $oid_want =~ /\A0+\z/;
 
         $self->{oid_want} = $oid_want;
         $self->{out} = $out;
diff --git a/lib/PublicInbox/ViewVCS.pm b/lib/PublicInbox/ViewVCS.pm
index a6dbb9a9..ead8c2b4 100644
--- a/lib/PublicInbox/ViewVCS.pm
+++ b/lib/PublicInbox/ViewVCS.pm
@@ -112,8 +112,10 @@ sub show_other ($$$$) {
         $qsp->psgi_qx($env, undef, \&show_other_result, $ctx);
 }
 
+# user_cb for SolverGit, called as: user_cb->($result_or_error, $uarg)
 sub solve_result {
-        my ($ctx, $res, $log, $hints, $fn) = @_;
+        my ($res, $ctx) = @_;
+        my ($log, $hints, $fn) = delete @$ctx{qw(log hints fn)};
 
         unless (seek($log, 0, 0)) {
                 $ctx->{env}->{'psgi.errors'}->print("seek(log): $!\n");
@@ -192,21 +194,20 @@ sub solve_result {
 sub show ($$;$) {
         my ($ctx, $oid_b, $fn) = @_;
         my $qp = $ctx->{qp};
-        my $hints = {};
+        my $hints = $ctx->{hints} = {};
         while (my ($from, $to) = each %QP_MAP) {
                 defined(my $v = $qp->{$from}) or next;
                 $hints->{$to} = $v;
         }
 
-        my $log = tmpfile("solve.$oid_b");
-        my $solver = PublicInbox::SolverGit->new($ctx->{-inbox}, sub {
-                solve_result($ctx, $_[0], $log, $hints, $fn);
-        });
-
-        # PSGI server will call this and give us a callback
+        $ctx->{'log'} = tmpfile("solve.$oid_b");
+        $ctx->{fn} = $fn;
+        my $solver = PublicInbox::SolverGit->new($ctx->{-inbox},
+                                                \&solve_result, $ctx);
+        # PSGI server will call this immediately and give us a callback (-wcb)
         sub {
                 $ctx->{-wcb} = $_[0]; # HTTP write callback
-                $solver->solve($ctx->{env}, $log, $oid_b, $hints);
+                $solver->solve($ctx->{env}, $ctx->{log}, $oid_b, $hints);
         };
 }