File Coverage

blib/lib/IMAP/Query.pm
Criterion Covered Total %
statement 11 66 16.6
branch 0 30 0.0
condition 0 13 0.0
subroutine 4 8 50.0
pod 1 1 100.0
total 16 118 13.5


line stmt bran cond sub pod time code
1             package IMAP::Query;
2              
3 1     1   21518 use 5.006;
  1         4  
4 1     1   5 use strict;
  1         2  
  1         31  
5 1     1   5 use warnings FATAL => 'all';
  1         6  
  1         59  
6              
7             =head1 NAME
8              
9             IMAP::Query - Build IMAP search queries!
10              
11             =head1 VERSION
12              
13             Version 0.01
14              
15             =cut
16              
17             our $VERSION = '0.01';
18              
19 1     1   902 use Readonly;
  1         3151  
  1         776  
20             Readonly my @KEYWORDS => qw(
21             ALL
22             ANSWERED
23             BCC
24             BEFORE
25             BODY
26             CC
27             DELETED
28             DRAFT
29             FLAGGED
30             FROM
31             HEADER
32             KEYWORD
33             LARGER
34             NEW
35             NOT
36             OLD
37             ON
38             OR
39             RECENT
40             SEEN
41             SENTBEFORE
42             SENTON
43             SENTSINCE
44             SINCE
45             SMALLER
46             SUBJECT
47             TEXT
48             TO
49             UID
50             UNANSWERED
51             UNDELETED
52             UNDRAFT
53             UNFLAGGED
54             UNKEYWORD
55             UNSEEN
56             );
57              
58              
59             =head1 SYNOPSIS
60              
61             This module is for those of us that can't create Polish notation queries by
62             hand, L can help you create them using a syntax inspired by other
63             query builder modules such as L.
64              
65             use IMAP::Query qw(build_search_string);
66              
67             my $query = build_search_string(
68             BEFORE => strftime('%d-%b-%Y', localtime(parsedate('yesterday'))),
69             NOT => 'DELETED',
70             -or => [
71             FROM => 'test@example.com',
72             -and => [
73             FROM => 'other@example.com',
74             SUBJECT => 'TESTING',
75             ],
76             ],
77             );
78              
79             ... # Do something interesting with our $query
80              
81             =head1 EXPORT
82              
83             A list of functions that can be exported. You can delete this section if you
84             don't export anything, such as for a purely object-oriented module.
85              
86             =head1 SUBROUTINES/METHODS
87              
88             =head2 build_search_string()
89              
90             This method can be exported via your use statement.
91              
92             use IMAP::Query qw(build_search_string);
93              
94             It takes two arguments.
95              
96             my $query = build_search_string($query, $operator);
97              
98             =over
99              
100             =item $query
101              
102             This option is requried. It is a HASHREF that contains all the items you want
103             to include in your search string.
104              
105             =item $operator
106              
107             This argument is optional and defaults to 'AND'. You can use either 'AND' or
108             'OR'.
109              
110             =back
111              
112             =cut
113              
114             sub build_search_string
115             {
116 0     0 1   my @options = @_;
117              
118 0           return _build_search_string_recurse(\@options);
119             }
120              
121             sub _build_search_string_recurse
122             {
123 0     0     my $opts = shift;
124 0   0       my $op = shift || 'AND';
125              
126 0           my @options = @$opts;
127              
128 0           my $rv = '';
129              
130 0           while (@options) {
131 0           my $item = shift @options;
132              
133 0 0 0       if (ref $item eq 'ARRAY') {
    0 0        
    0          
    0          
134 0           my $value = _build_search_string_ARRAYREF($item, $op);
135 0 0         if (length $value) {
136 0           _maybe_add_space($rv);
137 0           $rv .= $value;
138             }
139             }
140             elsif (ref $item eq 'HASH') {
141 0           _maybe_add_space($rv);
142 0           $rv .= _build_search_string_recurse([ %$item ], $op);
143             }
144             elsif (defined $item && $item =~ /^-/) {
145 0 0         if ($item =~ /^-and$/) {
    0          
146 0           $op = 'AND';
147             }
148             elsif ($item =~ /^-or$/) {
149 0           $op = 'OR';
150             }
151             else {
152 0           die "Unknown operator $item.\n";
153             }
154             }
155             elsif (defined $item && length $item) {
156 0           _maybe_add_space($rv);
157 0 0         if (any{ uc($item) eq $_ } @KEYWORDS) {
  0            
158 0           $rv .= uc($item);
159             }
160             else {
161 0           $rv .= qq/"$item"/;
162             }
163             }
164             }
165              
166 0           return ($op, $rv);
167             }
168              
169             sub _build_search_string_ARRAYREF
170             {
171 0     0     my $array = shift;
172 0   0       my $op = shift || 'AND';
173 0           my $local_op = $op;
174              
175 0           my $rv = '';
176 0 0         return $rv unless @$array;
177              
178 0           my @items;
179 0           foreach my $item (@$array) {
180 0           my $value = '';
181 0           ($local_op, $value) = _build_search_string_recurse([$item], $local_op);
182 0 0         if (length $value) {
183 0           push(@items, $value);
184             }
185             }
186 0 0         return $rv unless @items;
187              
188 0 0         if ($op eq 'AND') {
189 0           _maybe_add_space($rv);
190 0           $rv .= '(';
191 0           foreach my $item (@items) {
192 0           _maybe_add_space($rv);
193 0           $rv .= $item;
194             }
195 0           $rv .= ')';
196 0           return $rv;
197             }
198              
199 0 0         if (@items > 1) {
200 0           do {
201 0           my $or = '(OR '.shift(@items).' '.shift(@items).')';
202 0           push(@items, $or);
203             } while (@items >= 2);
204             }
205 0 0         if (@items) {
206 0           _maybe_add_space($rv);
207 0           $rv .= $items[0];
208             }
209              
210 0           return $rv;
211             }
212              
213             sub _maybe_add_space
214             {
215 0 0 0 0     if (length $_[0] && substr($_[0], -1, 1) !~ /^[\(\s]$/) {
216 0           $_[0] .= ' ';
217             }
218             }
219              
220             =head1 AUTHOR
221              
222             Adam R. Schobelock, C<< >>
223              
224             =head1 BUGS
225              
226             Please report any bugs or feature requests to C, or through
227             the web interface at L. I will be notified, and then you'll
228             automatically be notified of progress on your bug as I make changes.
229              
230             =head1 SUPPORT
231              
232             You can find documentation for this module with the perldoc command.
233              
234             perldoc IMAP::Query
235              
236             You can also look for information at:
237              
238             =over 4
239              
240             =item * RT: CPAN's request tracker (report bugs here)
241              
242             L
243              
244             =item * AnnoCPAN: Annotated CPAN documentation
245              
246             L
247              
248             =item * CPAN Ratings
249              
250             L
251              
252             =item * Search CPAN
253              
254             L
255              
256             =item * Code Repository
257              
258             L
259              
260             =back
261              
262             =head1 ACKNOWLEDGEMENTS
263              
264             =head1 LICENSE AND COPYRIGHT
265              
266             Copyright 2015 Adam R. Schobelock.
267              
268             This program is free software; you can redistribute it and/or modify it
269             under the terms of the the Artistic License (2.0). You may obtain a
270             copy of the full license at:
271              
272             L
273              
274             Any use, modification, and distribution of the Standard or Modified
275             Versions is governed by this Artistic License. By using, modifying or
276             distributing the Package, you accept this license. Do not use, modify,
277             or distribute the Package, if you do not accept this license.
278              
279             If your Modified Version has been derived from a Modified Version made
280             by someone other than you, you are nevertheless required to ensure that
281             your Modified Version complies with the requirements of this license.
282              
283             This license does not grant you the right to use any trademark, service
284             mark, tradename, or logo of the Copyright Holder.
285              
286             This license includes the non-exclusive, worldwide, free-of-charge
287             patent license to make, have made, use, offer to sell, sell, import and
288             otherwise transfer the Package with respect to any patent claims
289             licensable by the Copyright Holder that are necessarily infringed by the
290             Package. If you institute patent litigation (including a cross-claim or
291             counterclaim) against any party alleging that the Package constitutes
292             direct or contributory patent infringement, then this Artistic License
293             to you shall terminate on the date that such litigation is filed.
294              
295             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
296             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
297             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
298             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
299             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
300             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
301             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
302             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
303              
304             =cut
305              
306             1; # End of IMAP::Query