]> git.donarmstrong.com Git - debbugs.git/blob - Debbugs/Bugs.pm
Add bug searching abstraction
[debbugs.git] / Debbugs / Bugs.pm
1
2 package Debbugs::Bugs;
3
4 =head1 NAME
5
6 Debbugs::Bugs -- Bug selection routines for debbugs
7
8 =head1 SYNOPSIS
9
10 use Debbugs::Bugs qw(get_bugs);
11
12
13 =head1 DESCRIPTION
14
15 This module is a replacement for all of the various methods of
16 selecting different types of bugs.
17
18 It implements a single function, get_bugs, which defines the master
19 interface for selecting bugs.
20
21 It attempts to use subsidiary functions to actually do the selection,
22 in the order specified in the configuration files. [Unless you're
23 insane, they should be in order from fastest (and often most
24 incomplete) to slowest (and most complete).]
25
26 =head1 BUGS
27
28 =head1 FUNCTIONS
29
30 =cut
31
32 use warnings;
33 use strict;
34 use vars qw($VERSION $DEBUG %EXPORT_TAGS @EXPORT_OK @EXPORT);
35 use base qw(Exporter);
36
37 BEGIN{
38      $VERSION = 1.00;
39      $DEBUG = 0 unless defined $DEBUG;
40
41      @EXPORT = ();
42      %EXPORT_TAGS = ();
43      @EXPORT_OK = (qw(get_bugs));
44      $EXPORT_TAGS{all} = [@EXPORT_OK];
45 }
46
47 use Debbugs::Config qw(:config);
48 use Params::Validate qw(validate_with :types);
49 use IO::File;
50 use Debbugs::Status;
51 use Debbugs::Packages qw(getsrcpkgs);
52
53 =head2 get_bugs
54
55      get_bugs()
56
57 =head3 Parameters
58
59 The following parameters can either be a single scalar or a reference
60 to an array. The parameters are ANDed together, and the elements of
61 arrayrefs are a parameter are ORed. Future versions of this may allow
62 for limited regular expressions.
63
64 =over
65
66 =item package -- name of the binary package
67
68 =item src -- name of the source package
69
70 =item maint -- address of the maintainer
71
72 =item maintenc -- encoded address of the maintainer
73
74 =item submitter -- address of the submitter
75
76 =item severity -- severity of the bug
77
78 =item status -- status of the bug
79
80 =item tag -- bug tags
81
82 =item owner -- owner of the bug
83
84 =item dist -- distribution (I don't know about this one yet)
85
86 =item bugs -- list of bugs to search within
87
88 =back
89
90 =head3 Special options
91
92 The following options are special options used to modulate how the
93 searches are performed.
94
95 =over
96
97 =item archive -- whether to search archived bugs or normal bugs;
98 defaults to false.
99
100 =item usertags -- set of usertags and the bugs they are applied to
101
102 =back
103
104
105 =head3 Subsidiary routines
106
107 All subsidiary routines get passed exactly the same set of options as
108 get_bugs. If for some reason they are unable to handle the options
109 passed (for example, they don't have the right type of index for the
110 type of selection) they should die as early as possible. [Using
111 Params::Validate and/or die when files don't exist makes this fairly
112 trivial.]
113
114 This function will then immediately move on to the next subroutine,
115 giving it the same arguments.
116
117 =cut
118
119 sub get_bugs{
120      my %param = validate_with(params => \@_,
121                                spec   => {package   => {type => SCALAR|ARRAYREF,
122                                                         optional => 1,
123                                                        },
124                                           src       => {type => SCALAR|ARRAYREF,
125                                                         optional => 1,
126                                                        },
127                                           maint     => {type => SCALAR|ARRAYREF,
128                                                         optional => 1,
129                                                        },
130                                           maintenc  => {type => SCALAR|ARRAYREF,
131                                                         optional => 1,
132                                                        },
133                                           submitter => {type => SCALAR|ARRAYREF,
134                                                         optional => 1,
135                                                        },
136                                           severity  => {type => SCALAR|ARRAYREF,
137                                                         optional => 1,
138                                                        },
139                                           status    => {type => SCALAR|ARRAYREF,
140                                                         optional => 1,
141                                                        },
142                                           tag       => {type => SCALAR|ARRAYREF,
143                                                         optional => 1,
144                                                        },
145                                           owner     => {type => SCALAR|ARRAYREF,
146                                                         optional => 1,
147                                                        },
148                                           dist      => {type => SCALAR|ARRAYREF,
149                                                         optional => 1,
150                                                        },
151                                           bugs      => {type => SCALAR|ARRAYREF,
152                                                         optional => 1,
153                                                        },
154                                           archive   => {type => BOOLEAN,
155                                                         default => 0,
156                                                        },
157                                           usertags  => {type => HASHREF,
158                                                         optional => 1,
159                                                        },
160                                          },
161                               );
162
163      # Normalize options
164      my %options = %param;
165      my @bugs;
166      # A configuration option will set an array that we'll use here instead.
167      for my $routine (qw(Debbugs::Bugs::get_bugs_flatfile)) {
168           my ($package) = $routine =~ m/^(.+)\:\:/;
169           eval "use $package;";
170           if ($@) {
171                # We output errors here because using an invalid function
172                # in the configuration file isn't something that should
173                # be done.
174                warn "use $package failed with $@";
175                next;
176           }
177           @bugs = eval "${routine}(\%options)";
178           if ($@) {
179
180                # We don't output errors here, because failure here
181                # via die may be a perfectly normal thing.
182                print STDERR "$@" if $DEBUG;
183                next;
184           }
185           last;
186      }
187      # If no one succeeded, die
188      if ($@) {
189           die "$@";
190      }
191      return @bugs;
192 }
193
194 sub get_bugs_flatfile{
195      my %param = validate_with(params => \@_,
196                                spec   => {package   => {type => SCALAR|ARRAYREF,
197                                                         optional => 1,
198                                                        },
199                                           src       => {type => SCALAR|ARRAYREF,
200                                                         optional => 1,
201                                                        },
202                                           maint     => {type => SCALAR|ARRAYREF,
203                                                         optional => 1,
204                                                        },
205                                           maintenc  => {type => SCALAR|ARRAYREF,
206                                                         optional => 1,
207                                                        },
208                                           submitter => {type => SCALAR|ARRAYREF,
209                                                         optional => 1,
210                                                        },
211                                           severity  => {type => SCALAR|ARRAYREF,
212                                                         optional => 1,
213                                                        },
214                                           status    => {type => SCALAR|ARRAYREF,
215                                                         optional => 1,
216                                                        },
217                                           tag       => {type => SCALAR|ARRAYREF,
218                                                         optional => 1,
219                                                        },
220 # not yet supported
221 #                                         owner     => {type => SCALAR|ARRAYREF,
222 #                                                       optional => 1,
223 #                                                      },
224 #                                         dist      => {type => SCALAR|ARRAYREF,
225 #                                                       optional => 1,
226 #                                                      },
227                                           archive   => {type => BOOLEAN,
228                                                         default => 1,
229                                                        },
230                                           usertags  => {type => HASHREF,
231                                                         optional => 1,
232                                                        },
233                                          },
234                               );
235      my $flatfile;
236      if ($param{archive}) {
237           $flatfile = new IO::File "$debbugs::gSpoolDir/index.archive", 'r'
238                or die "Unable to open $debbugs::gSpoolDir/index.archive for reading: $!";
239      }
240      else {
241           $flatfile = new IO::File "$debbugs::gSpoolDir/index.db", 'r'
242                or die "Unable to open $debbugs::gSpoolDir/index.db for reading: $!";
243      }
244      my %usertag_bugs;
245      if (exists $param{tag} and exists $param{usertags}) {
246
247           # This complex slice makes a hash with the bugs which have the
248           # usertags passed in $param{tag} set.
249           @usertag_bugs{map {@{$_}}
250                              @{$param{usertags}}{__make_list($param{tag})}
251                         } = (1) x @{$param{usertags}}{__make_list($param{tag})}
252      }
253      my @bugs;
254      while (<$flatfile>) {
255           next unless m/^(\S+)\s+(\d+)\s+(\d+)\s+(\S+)\s+\[\s*([^]]*)\s*\]\s+(\w+)\s+(.*)$/;
256           my ($pkg,$bug,$status,$submitter,$severity,$tags) = ($1,$2,$3,$4,$5,$6,$7);
257           next if exists $param{bug} and not grep {$bug == $_} __make_list($param{bugs});
258           if (exists $param{pkg}) {
259                my @packages = splitpackages($pkg);
260                next unless grep { my $pkg_list = $_;
261                                   grep {$pkg_list eq $_} __make_list($param{pkg})
262                              } @packages;
263           }
264           if (exists $param{src}) {
265                my @src_packages = map { getsrcpkgs($_)} __make_list($param{src});
266                my @packages = splitpackages($pkg);
267                next unless grep { my $pkg_list = $_;
268                                   grep {$pkg_list eq $_} @packages
269                              } @src_packages;
270           }
271           if (exists $param{submitter}) {
272                my @p_addrs = map {$_->address}
273                     map {lc(getparsedaddrs($_))}
274                          __make_list($param{submitter});
275                my @f_addrs = map {$_->address}
276                     getparsedaddrs($submitter||'');
277                next unless grep { my $f_addr = $_; 
278                                   grep {$f_addr eq $_} @p_addrs
279                              } @f_addrs;
280           }
281           next if exists $param{severity} and not grep {$severity eq $_} __make_list($param{severity});
282           next if exists $param{status} and not grep {$status eq $_} __make_list($param{status});
283           if (exists $param{tag}) {
284                my $bug_ok = 0;
285                # either a normal tag, or a usertag must be set
286                $bug_ok = 1 if exists $param{usertags} and $usertag_bugs{$bug};
287                my @bug_tags = split ' ', $tags;
288                $bug_ok = 1 if grep {my $bug_tag = $_;
289                                     grep {$bug_tag eq $_} __make_list($param{tag});
290                                } @bug_tags;
291                next unless $bug_ok;
292           }
293           push @bugs, $bug;
294      }
295      return @bugs;
296 }
297
298
299 # This private subroutine takes a scalar and turns it
300 # into a list; transforming arrayrefs into their contents
301 # along the way.
302 sub __make_list{
303      return map {ref($_) eq 'ARRAY'?@{$_}:$_} @_;
304 }
305
306 1;
307
308 __END__