]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/LeiMailDiff.pm
4f3a4608d8e0beceafda3830b9fff41ffbae6b4d
[public-inbox.git] / lib / PublicInbox / LeiMailDiff.pm
1 # Copyright (C) all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3
4 # The "lei mail-diff" sub-command, diffs input contents against
5 # the first message of input
6 package PublicInbox::LeiMailDiff;
7 use strict;
8 use v5.10.1;
9 use parent qw(PublicInbox::IPC PublicInbox::LeiInput);
10 use File::Temp 0.19 (); # 0.19 for ->newdir
11 use PublicInbox::Spawn qw(spawn which);
12 use PublicInbox::MsgIter qw(msg_part_text);
13 use File::Path qw(remove_tree);
14 use PublicInbox::ContentHash qw(content_digest);
15 require PublicInbox::LeiRediff;
16 use Data::Dumper ();
17
18 sub write_part { # Eml->each_part callback
19         my ($ary, $self) = @_;
20         my ($part, $depth, $idx) = @$ary;
21         if ($idx ne '1' || $self->{lei}->{opt}->{'raw-header'}) {
22                 open my $fh, '>', "$self->{curdir}/$idx.hdr" or die "open: $!";
23                 print $fh ${$part->{hdr}} or die "print $!";
24                 close $fh or die "close $!";
25         }
26         my $ct = $part->content_type || 'text/plain';
27         my ($s, $err) = msg_part_text($part, $ct);
28         my $sfx = defined($s) ? 'txt' : 'bin';
29         open my $fh, '>', "$self->{curdir}/$idx.$sfx" or die "open: $!";
30         print $fh ($s // $part->body) or die "print $!";
31         close $fh or die "close $!";
32 }
33
34 sub dump_eml ($$$) {
35         my ($self, $dir, $eml) = @_;
36         local $self->{curdir} = $dir;
37         mkdir $dir or die "mkdir($dir): $!";
38         $eml->each_part(\&write_part, $self);
39
40         open my $fh, '>', "$dir/content_digest" or die "open: $!";
41         my $dig = PublicInbox::ContentDigestDbg->new($fh);
42         local $Data::Dumper::Useqq = 1;
43         local $Data::Dumper::Terse = 1;
44         content_digest($eml, $dig);
45         print $fh "\n", $dig->hexdigest, "\n" or die "print $!";
46         close $fh or die "close: $!";
47 }
48
49 sub prep_a ($$) {
50         my ($self, $eml) = @_;
51         $self->{tmp} = File::Temp->newdir('lei-mail-diff-XXXX', TMPDIR => 1);
52         dump_eml($self, "$self->{tmp}/a", $eml);
53 }
54
55 sub diff_a ($$) {
56         my ($self, $eml) = @_;
57         ++$self->{nr};
58         my $dir = "$self->{tmp}/N$self->{nr}";
59         dump_eml($self, $dir, $eml);
60         my $cmd = [ qw(git diff --no-index) ];
61         my $lei = $self->{lei};
62         PublicInbox::LeiRediff::_lei_diff_prepare($lei, $cmd);
63         push @$cmd, qw(-- a), "N$self->{nr}";
64         my $rdr = { -C => "$self->{tmp}" };
65         @$rdr{1, 2} = @$lei{1, 2};
66         my $pid = spawn($cmd, $lei->{env}, $rdr);
67         waitpid($pid, 0);
68         $lei->child_error($?) if $?; # for git diff --exit-code
69         File::Path::remove_tree($self->{curdir});
70 }
71
72 sub input_eml_cb { # used by PublicInbox::LeiInput::input_fh
73         my ($self, $eml) = @_;
74         $self->{tmp} ? diff_a($self, $eml) : prep_a($self, $eml);
75 }
76
77 sub lei_mail_diff {
78         my ($lei, @argv) = @_;
79         $lei->{opt}->{'in-format'} //= 'eml';
80         my $self = bless {}, __PACKAGE__;
81         $self->prepare_inputs($lei, \@argv) or return;
82         my $isatty = -t $lei->{1};
83         $lei->{opt}->{color} //= $isatty;
84         $lei->start_pager if $isatty;
85         my $ops = {};
86         $lei->{auth}->op_merge($ops, $self, $lei) if $lei->{auth};
87         (my $op_c, $ops) = $lei->workers_start($self, 1, $ops);
88         $lei->{wq1} = $self;
89         $lei->{-err_type} = 'non-fatal';
90         net_merge_all_done($self) unless $lei->{auth};
91         $lei->wait_wq_events($op_c, $ops);
92 }
93
94 no warnings 'once';
95 *net_merge_all_done = \&PublicInbox::LeiInput::input_only_net_merge_all_done;
96
97 package PublicInbox::ContentDigestDbg;
98 use strict;
99 use v5.10.1;
100 use Data::Dumper;
101
102 sub new { bless { dig => Digest::SHA->new(256), fh => $_[1] }, __PACKAGE__ }
103
104 sub add {
105         $_[0]->{dig}->add($_[1]);
106         print { $_[0]->{fh} } Dumper($_[1]) or die "print $!";
107 }
108
109 sub hexdigest { $_[0]->{dig}->hexdigest; }
110
111 1;