net-imap 0.5.7 → 0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/lib/net/imap/config/attr_type_coercion.rb +5 -1
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +0 -1
- data/lib/net/imap/sequence_set.rb +492 -134
- data/lib/net/imap.rb +46 -16
- data/rakelib/string_prep_tables_generator.rb +4 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 261794e07175481d35e146b718183fc057a3a54e2fd9958b25918c6bd4178ec4
|
4
|
+
data.tar.gz: 2568eb1d284b3f1662d1cf0c1bc79eb65722fa3ba8dc03072b52fbd3716c9294
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 357279f77c69c27b78924847216afad6c806abb7bfee5f0c95aab6c397cb10298440a59cb32b9f75a4174e6965d6bfe48a5e847e20d9098845033e8453f73302
|
7
|
+
data.tar.gz: e3c29888d787de23a843bfb9ff9194d1fa136763e9d9bcf36800953362f7611c489da1f9150ee2a23baa571125f4f2af2cebe4ee4666447783e355bd9995eb3a
|
data/Gemfile
CHANGED
@@ -14,7 +14,9 @@ gem "rdoc"
|
|
14
14
|
gem "test-unit"
|
15
15
|
gem "test-unit-ruby-core", git: "https://github.com/ruby/test-unit-ruby-core"
|
16
16
|
|
17
|
+
gem "benchmark", require: false
|
17
18
|
gem "benchmark-driver", require: false
|
19
|
+
gem "vernier", require: false, platform: :mri
|
18
20
|
|
19
21
|
group :test do
|
20
22
|
gem "simplecov", require: false
|
@@ -28,7 +28,11 @@ module Net
|
|
28
28
|
end
|
29
29
|
private_class_method :included
|
30
30
|
|
31
|
-
|
31
|
+
if defined?(Ractor.make_shareable)
|
32
|
+
def self.safe(...) Ractor.make_shareable nil.instance_eval(...).freeze end
|
33
|
+
else
|
34
|
+
def self.safe(...) nil.instance_eval(...).freeze end
|
35
|
+
end
|
32
36
|
private_class_method :safe
|
33
37
|
|
34
38
|
Types = Hash.new do |h, type| type => Proc | nil; safe{type} end
|
data/lib/net/imap/flags.rb
CHANGED
@@ -6,7 +6,6 @@ module Net
|
|
6
6
|
autoload :FetchData, "#{__dir__}/fetch_data"
|
7
7
|
autoload :UIDFetchData, "#{__dir__}/fetch_data"
|
8
8
|
autoload :SearchResult, "#{__dir__}/search_result"
|
9
|
-
autoload :SequenceSet, "#{__dir__}/sequence_set"
|
10
9
|
autoload :UIDPlusData, "#{__dir__}/uidplus_data"
|
11
10
|
autoload :AppendUIDData, "#{__dir__}/uidplus_data"
|
12
11
|
autoload :CopyUIDData, "#{__dir__}/uidplus_data"
|
@@ -18,21 +18,9 @@ module Net
|
|
18
18
|
#
|
19
19
|
# == Creating sequence sets
|
20
20
|
#
|
21
|
-
# SequenceSet.new with no arguments creates an empty sequence set. Note
|
22
|
-
# that an empty sequence set is invalid in the \IMAP grammar.
|
23
|
-
#
|
24
|
-
# set = Net::IMAP::SequenceSet.new
|
25
|
-
# set.empty? #=> true
|
26
|
-
# set.valid? #=> false
|
27
|
-
# set.valid_string #!> raises DataFormatError
|
28
|
-
# set << 1..10
|
29
|
-
# set.empty? #=> false
|
30
|
-
# set.valid? #=> true
|
31
|
-
# set.valid_string #=> "1:10"
|
32
|
-
#
|
33
21
|
# SequenceSet.new may receive a single optional argument: a non-zero 32 bit
|
34
22
|
# unsigned integer, a range, a <tt>sequence-set</tt> formatted string,
|
35
|
-
# another
|
23
|
+
# another SequenceSet, a Set (containing only numbers or <tt>*</tt>), or an
|
36
24
|
# Array containing any of these (array inputs may be nested).
|
37
25
|
#
|
38
26
|
# set = Net::IMAP::SequenceSet.new(1)
|
@@ -48,30 +36,114 @@ module Net
|
|
48
36
|
# set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
|
49
37
|
# set.valid_string #=> "1:10,55,1024:2048"
|
50
38
|
#
|
51
|
-
#
|
52
|
-
#
|
39
|
+
# SequenceSet.new with no arguments creates an empty sequence set. Note
|
40
|
+
# that an empty sequence set is invalid in the \IMAP grammar.
|
41
|
+
#
|
42
|
+
# set = Net::IMAP::SequenceSet.new
|
43
|
+
# set.empty? #=> true
|
44
|
+
# set.valid? #=> false
|
45
|
+
# set.valid_string #!> raises DataFormatError
|
46
|
+
# set << 1..10
|
47
|
+
# set.empty? #=> false
|
48
|
+
# set.valid? #=> true
|
49
|
+
# set.valid_string #=> "1:10"
|
53
50
|
#
|
51
|
+
# Using SequenceSet.new with another SequenceSet input behaves the same as
|
52
|
+
# calling #dup on the other set. The input's #string will be preserved.
|
53
|
+
#
|
54
|
+
# input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
|
55
|
+
# copy = Net::IMAP::SequenceSet.new(input)
|
56
|
+
# input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
57
|
+
# copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
58
|
+
# copy2 = input.dup # same as calling new with a SequenceSet input
|
59
|
+
# copy == input #=> true, same set membership
|
60
|
+
# copy.eql? input #=> true, same string value
|
61
|
+
# copy.equal? input #=> false, different objects
|
62
|
+
#
|
63
|
+
# copy.normalize!
|
64
|
+
# copy.valid_string #=> "1:10,1024,2048"
|
65
|
+
# copy == input #=> true, same set membership
|
66
|
+
# copy.eql? input #=> false, different string value
|
67
|
+
#
|
68
|
+
# copy << 999
|
69
|
+
# copy.valid_string #=> "1:10,999,1024,2048"
|
70
|
+
# copy == input #=> false, different set membership
|
71
|
+
# copy.eql? input #=> false, different string value
|
72
|
+
#
|
73
|
+
# Use Net::IMAP::SequenceSet() to coerce a single (optional) input.
|
74
|
+
# A SequenceSet input is returned without duplication, even when frozen.
|
75
|
+
#
|
76
|
+
# set = Net::IMAP::SequenceSet()
|
77
|
+
# set.string #=> nil
|
78
|
+
# set.frozen? #=> false
|
79
|
+
#
|
80
|
+
# # String order is preserved
|
81
|
+
# set = Net::IMAP::SequenceSet("1,2,3:7,5,6:10,2048,1024")
|
82
|
+
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
83
|
+
# set.frozen? #=> false
|
84
|
+
#
|
85
|
+
# # Other inputs are normalized
|
86
|
+
# set = Net::IMAP::SequenceSet([1, 2, [3..7, 5], 6..10, 2048, 1024])
|
87
|
+
# set.valid_string #=> "1:10,1024,2048"
|
88
|
+
# set.frozen? #=> false
|
89
|
+
#
|
90
|
+
# unfrozen = set
|
91
|
+
# frozen = set.dup.freeze
|
92
|
+
# unfrozen.equal? Net::IMAP::SequenceSet(unfrozen) #=> true
|
93
|
+
# frozen.equal? Net::IMAP::SequenceSet(frozen) #=> true
|
94
|
+
#
|
95
|
+
# Use ::[] to coerce one or more arguments into a valid frozen SequenceSet.
|
96
|
+
# A valid frozen SequenceSet is returned directly, without allocating a new
|
97
|
+
# object. ::[] will not create an invalid (empty) set.
|
98
|
+
#
|
99
|
+
# Net::IMAP::SequenceSet[] #!> raises ArgumentError
|
100
|
+
# Net::IMAP::SequenceSet[nil] #!> raises DataFormatError
|
101
|
+
# Net::IMAP::SequenceSet[""] #!> raises DataFormatError
|
102
|
+
#
|
103
|
+
# # String order is preserved
|
54
104
|
# set = Net::IMAP::SequenceSet["1,2,3:7,5,6:10,2048,1024"]
|
55
105
|
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
106
|
+
# set.frozen? #=> true
|
107
|
+
#
|
108
|
+
# # Other inputs are normalized
|
56
109
|
# set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
|
57
|
-
# set.valid_string #=> "1:10,
|
110
|
+
# set.valid_string #=> "1:10,1024,2048"
|
111
|
+
# set.frozen? #=> true
|
112
|
+
#
|
113
|
+
# frozen = set
|
114
|
+
# unfrozen = set.dup
|
115
|
+
# frozen.equal? Net::IMAP::SequenceSet[frozen] #=> true
|
116
|
+
# unfrozen.equal? Net::IMAP::SequenceSet[unfrozen] #=> false
|
117
|
+
#
|
118
|
+
# Objects which respond to +to_sequence_set+ (such as SearchResult and
|
119
|
+
# ThreadMember) can be coerced to a SequenceSet with ::new, ::try_convert,
|
120
|
+
# ::[], or Net::IMAP::SequenceSet.
|
121
|
+
#
|
122
|
+
# search = imap.uid_search(["SUBJECT", "hello", "NOT", "SEEN"])
|
123
|
+
# seqset = Net::IMAP::SequenceSet(search) - already_fetched
|
124
|
+
# fetch = imap.uid_fetch(seqset, "FAST")
|
58
125
|
#
|
59
126
|
# == Ordered and Normalized sets
|
60
127
|
#
|
61
128
|
# Sometimes the order of the set's members is significant, such as with the
|
62
129
|
# +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
|
63
|
-
# sequence set is created
|
64
|
-
# #string representation is preserved.
|
130
|
+
# sequence set is created from a single string (such as by the parser), that
|
131
|
+
# #string representation is preserved. Assigning a string with #string= or
|
132
|
+
# #replace will also preserve that string. Use #each_entry, #entries, or
|
133
|
+
# #each_ordered_number to enumerate the entries in their #string order.
|
134
|
+
# Hash equality (using #eql?) is based on the string representation.
|
65
135
|
#
|
66
|
-
# Internally, SequenceSet
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
# the set in
|
136
|
+
# Internally, SequenceSet uses a normalized uint32 set representation which
|
137
|
+
# sorts and de-duplicates all numbers and coalesces adjacent or overlapping
|
138
|
+
# entries. Many methods use this sorted set representation for <tt>O(lg
|
139
|
+
# n)</tt> searches. Use #each_element, #elements, #each_range, #ranges,
|
140
|
+
# #each_number, or #numbers to enumerate the set in sorted order. Basic
|
141
|
+
# object equality (using #==) is based on set membership, without regard to
|
142
|
+
# #entry order or #string normalization.
|
71
143
|
#
|
72
|
-
# Most modification methods
|
73
|
-
#
|
74
|
-
#
|
144
|
+
# Most modification methods reset #string to its #normalized form, so that
|
145
|
+
# #entries and #elements are identical. Use #append to preserve #entries
|
146
|
+
# order while modifying a set.
|
75
147
|
#
|
76
148
|
# == Using <tt>*</tt>
|
77
149
|
#
|
@@ -108,11 +180,15 @@ module Net
|
|
108
180
|
# When a set includes <tt>*</tt>, some methods may have surprising behavior.
|
109
181
|
#
|
110
182
|
# For example, #complement treats <tt>*</tt> as its own number. This way,
|
111
|
-
# the #intersection of a set and its #complement will always be empty.
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
183
|
+
# the #intersection of a set and its #complement will always be empty. And
|
184
|
+
# <tt>*</tt> is sorted as greater than any other number in the set. This is
|
185
|
+
# not how an \IMAP server interprets the set: it will convert <tt>*</tt> to
|
186
|
+
# the number of messages in the mailbox, the +UID+ of the last message in
|
187
|
+
# the mailbox, or +UIDNEXT+, as appropriate. Several methods have an
|
188
|
+
# argument for how <tt>*</tt> should be interpreted.
|
189
|
+
#
|
190
|
+
# But, for example, this means that there may be overlap between a set and
|
191
|
+
# its complement after #limit is applied to each:
|
116
192
|
#
|
117
193
|
# ~Net::IMAP::SequenceSet["*"] == Net::IMAP::SequenceSet[1..(2**32-1)]
|
118
194
|
# ~Net::IMAP::SequenceSet[1..5] == Net::IMAP::SequenceSet["6:*"]
|
@@ -149,6 +225,7 @@ module Net
|
|
149
225
|
# * ::new: Creates a new mutable sequence set, which may be empty (invalid).
|
150
226
|
# * ::try_convert: Calls +to_sequence_set+ on an object and verifies that
|
151
227
|
# the result is a SequenceSet.
|
228
|
+
# * Net::IMAP::SequenceSet(): Coerce an input using ::try_convert or ::new.
|
152
229
|
# * ::empty: Returns a frozen empty (invalid) SequenceSet.
|
153
230
|
# * ::full: Returns a frozen SequenceSet containing every possible number.
|
154
231
|
#
|
@@ -174,14 +251,13 @@ module Net
|
|
174
251
|
#
|
175
252
|
# <i>Set membership:</i>
|
176
253
|
# - #include? (aliased as #member?):
|
177
|
-
# Returns whether a given element
|
178
|
-
# contained by the set.
|
254
|
+
# Returns whether a given element is contained by the set.
|
179
255
|
# - #include_star?: Returns whether the set contains <tt>*</tt>.
|
180
256
|
#
|
181
257
|
# <i>Minimum and maximum value elements:</i>
|
182
|
-
# - #min: Returns the
|
183
|
-
# - #max: Returns the
|
184
|
-
# - #minmax: Returns the
|
258
|
+
# - #min: Returns one or more of the lowest numbers in the set.
|
259
|
+
# - #max: Returns one or more of the highest numbers in the set.
|
260
|
+
# - #minmax: Returns the lowest and highest numbers in the set.
|
185
261
|
#
|
186
262
|
# <i>Accessing value by offset in sorted set:</i>
|
187
263
|
# - #[] (aliased as #slice): Returns the number or consecutive subset at a
|
@@ -248,6 +324,10 @@ module Net
|
|
248
324
|
# +self+ and the other set except those common to both.
|
249
325
|
# - #~ (aliased as #complement): Returns a new set containing all members
|
250
326
|
# that are not in +self+
|
327
|
+
# - #above: Return a copy of +self+ which only contains numbers above a
|
328
|
+
# given number.
|
329
|
+
# - #below: Return a copy of +self+ which only contains numbers below a
|
330
|
+
# given value.
|
251
331
|
# - #limit: Returns a copy of +self+ which has replaced <tt>*</tt> with a
|
252
332
|
# given maximum value and removed all members over that maximum.
|
253
333
|
#
|
@@ -316,6 +396,23 @@ module Net
|
|
316
396
|
STARS = [:*, ?*, -1].freeze
|
317
397
|
private_constant :STARS
|
318
398
|
|
399
|
+
INSPECT_MAX_LEN = 512
|
400
|
+
INSPECT_TRUNCATE_LEN = 16
|
401
|
+
private_constant :INSPECT_MAX_LEN, :INSPECT_TRUNCATE_LEN
|
402
|
+
|
403
|
+
# /(,\d+){100}\z/ is shockingly slow on huge strings.
|
404
|
+
# /(,\d{0,10}){100}\z/ is ok, but ironically, Regexp.linear_time? is false.
|
405
|
+
#
|
406
|
+
# This unrolls all nested quantifiers. It's much harder to read, but it's
|
407
|
+
# also the fastest out of all the versions I tested.
|
408
|
+
nz_uint32 = /[1-9](?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d)?)?)?)?)?)?)?)?)?/
|
409
|
+
num_or_star = /#{nz_uint32}|\*/
|
410
|
+
entry = /#{num_or_star}(?::#{num_or_star})?/
|
411
|
+
entries = ([entry] * INSPECT_TRUNCATE_LEN).join(",")
|
412
|
+
INSPECT_ABRIDGED_HEAD_RE = /\A#{entries},/
|
413
|
+
INSPECT_ABRIDGED_TAIL_RE = /,#{entries}\z/
|
414
|
+
private_constant :INSPECT_ABRIDGED_HEAD_RE, :INSPECT_ABRIDGED_TAIL_RE
|
415
|
+
|
319
416
|
class << self
|
320
417
|
|
321
418
|
# :call-seq:
|
@@ -329,13 +426,12 @@ module Net
|
|
329
426
|
# An empty SequenceSet is invalid and will raise a DataFormatError.
|
330
427
|
#
|
331
428
|
# Use ::new to create a mutable or empty SequenceSet.
|
429
|
+
#
|
430
|
+
# Related: ::new, Net::IMAP::SequenceSet(), ::try_convert
|
332
431
|
def [](first, *rest)
|
333
432
|
if rest.empty?
|
334
|
-
|
335
|
-
|
336
|
-
else
|
337
|
-
new(first).validate.freeze
|
338
|
-
end
|
433
|
+
set = try_convert(first)&.validate
|
434
|
+
set&.frozen? ? set : (set&.dup || new(first).validate).freeze
|
339
435
|
else
|
340
436
|
new(first).merge(*rest).validate.freeze
|
341
437
|
end
|
@@ -350,6 +446,8 @@ module Net
|
|
350
446
|
#
|
351
447
|
# If +obj.to_sequence_set+ doesn't return a SequenceSet, an exception is
|
352
448
|
# raised.
|
449
|
+
#
|
450
|
+
# Related: Net::IMAP::SequenceSet(), ::new, ::[]
|
353
451
|
def try_convert(obj)
|
354
452
|
return obj if obj.is_a?(SequenceSet)
|
355
453
|
return nil unless obj.respond_to?(:to_sequence_set)
|
@@ -368,23 +466,91 @@ module Net
|
|
368
466
|
end
|
369
467
|
|
370
468
|
# Create a new SequenceSet object from +input+, which may be another
|
371
|
-
# SequenceSet, an IMAP formatted +sequence-set+ string, a
|
372
|
-
# range, <tt>:*</tt>,
|
373
|
-
#
|
374
|
-
#
|
469
|
+
# SequenceSet, an IMAP formatted +sequence-set+ string, a non-zero 32 bit
|
470
|
+
# unsigned integer, a range, <tt>:*</tt>, a Set of numbers or <tt>*</tt>,
|
471
|
+
# an object that responds to +to_sequence_set+ (such as SearchResult) or
|
472
|
+
# an Array of these (array inputs may be nested).
|
473
|
+
#
|
474
|
+
# set = Net::IMAP::SequenceSet.new(1)
|
475
|
+
# set.valid_string #=> "1"
|
476
|
+
# set = Net::IMAP::SequenceSet.new(1..100)
|
477
|
+
# set.valid_string #=> "1:100"
|
478
|
+
# set = Net::IMAP::SequenceSet.new(1...100)
|
479
|
+
# set.valid_string #=> "1:99"
|
480
|
+
# set = Net::IMAP::SequenceSet.new([1, 2, 5..])
|
481
|
+
# set.valid_string #=> "1:2,5:*"
|
482
|
+
# set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
|
483
|
+
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
484
|
+
# set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
|
485
|
+
# set.valid_string #=> "1:10,1024,2048"
|
486
|
+
#
|
487
|
+
# With no arguments (or +nil+) creates an empty sequence set. Note that
|
488
|
+
# an empty sequence set is invalid in the \IMAP grammar.
|
489
|
+
#
|
490
|
+
# set = Net::IMAP::SequenceSet.new
|
491
|
+
# set.empty? #=> true
|
492
|
+
# set.valid? #=> false
|
493
|
+
# set.valid_string #!> raises DataFormatError
|
494
|
+
# set << 1..10
|
495
|
+
# set.empty? #=> false
|
496
|
+
# set.valid? #=> true
|
497
|
+
# set.valid_string #=> "1:10"
|
498
|
+
#
|
499
|
+
# When +input+ is a SequenceSet, ::new behaves the same as calling #dup on
|
500
|
+
# that other set. The input's #string will be preserved.
|
501
|
+
#
|
502
|
+
# input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
|
503
|
+
# copy = Net::IMAP::SequenceSet.new(input)
|
504
|
+
# input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
505
|
+
# copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
506
|
+
# copy2 = input.dup # same as calling new with a SequenceSet input
|
507
|
+
# copy == input #=> true, same set membership
|
508
|
+
# copy.eql? input #=> true, same string value
|
509
|
+
# copy.equal? input #=> false, different objects
|
510
|
+
#
|
511
|
+
# copy.normalize!
|
512
|
+
# copy.valid_string #=> "1:10,1024,2048"
|
513
|
+
# copy == input #=> true, same set membership
|
514
|
+
# copy.eql? input #=> false, different string value
|
515
|
+
#
|
516
|
+
# copy << 999
|
517
|
+
# copy.valid_string #=> "1:10,999,1024,2048"
|
518
|
+
# copy == input #=> false, different set membership
|
519
|
+
# copy.eql? input #=> false, different string value
|
520
|
+
#
|
521
|
+
# === Alternative set creation methods
|
522
|
+
#
|
523
|
+
# * ::[] returns a frozen validated (non-empty) SequenceSet, without
|
524
|
+
# allocating a new object when the input is already a valid frozen
|
525
|
+
# SequenceSet.
|
526
|
+
# * Net::IMAP::SequenceSet() coerces an input to SequenceSet, without
|
527
|
+
# allocating a new object when the input is already a SequenceSet.
|
528
|
+
# * ::try_convert calls +to_sequence_set+ on inputs that support it and
|
529
|
+
# returns +nil+ for inputs that don't.
|
530
|
+
# * ::empty and ::full both return frozen singleton sets which can be
|
531
|
+
# combined with set operations (#|, #&, #^, #-, etc) to make new sets.
|
532
|
+
#
|
533
|
+
# See SequenceSet@Creating+sequence+sets.
|
375
534
|
def initialize(input = nil) input ? replace(input) : clear end
|
376
535
|
|
377
536
|
# Removes all elements and returns self.
|
378
|
-
def clear
|
537
|
+
def clear
|
538
|
+
modifying! # redundant check, to normalize the error message for JRuby
|
539
|
+
@tuples, @string = [], nil
|
540
|
+
self
|
541
|
+
end
|
379
542
|
|
380
543
|
# Replace the contents of the set with the contents of +other+ and returns
|
381
544
|
# +self+.
|
382
545
|
#
|
383
|
-
# +other+ may be another SequenceSet
|
384
|
-
#
|
546
|
+
# +other+ may be another SequenceSet or any other object that would be
|
547
|
+
# accepted by ::new.
|
385
548
|
def replace(other)
|
386
549
|
case other
|
387
|
-
when SequenceSet then
|
550
|
+
when SequenceSet then
|
551
|
+
modifying! # short circuit before doing any work
|
552
|
+
@tuples = other.deep_copy_tuples
|
553
|
+
@string = other.instance_variable_get(:@string)
|
388
554
|
when String then self.string = other
|
389
555
|
else clear; merge other
|
390
556
|
end
|
@@ -413,36 +579,40 @@ module Net
|
|
413
579
|
# If the set was created from a single string, it is not normalized. If
|
414
580
|
# the set is updated the string will be normalized.
|
415
581
|
#
|
416
|
-
# Related: #valid_string, #normalized_string, #to_s
|
582
|
+
# Related: #valid_string, #normalized_string, #to_s, #inspect
|
417
583
|
def string; @string ||= normalized_string if valid? end
|
418
584
|
|
419
585
|
# Returns an array with #normalized_string when valid and an empty array
|
420
586
|
# otherwise.
|
421
587
|
def deconstruct; valid? ? [normalized_string] : [] end
|
422
588
|
|
423
|
-
# Assigns a new string to #string and resets #elements to match.
|
424
|
-
#
|
425
|
-
# The string is validated but not normalized.
|
589
|
+
# Assigns a new string to #string and resets #elements to match.
|
590
|
+
# Assigning +nil+ or an empty string are equivalent to calling #clear.
|
426
591
|
#
|
427
|
-
#
|
592
|
+
# Non-empty strings are validated but not normalized.
|
593
|
+
#
|
594
|
+
# Use #add, #merge, or #append to add a string to an existing set.
|
428
595
|
#
|
429
596
|
# Related: #replace, #clear
|
430
|
-
def string=(
|
431
|
-
if
|
597
|
+
def string=(input)
|
598
|
+
if input.nil?
|
432
599
|
clear
|
433
|
-
|
434
|
-
|
600
|
+
elsif (str = String.try_convert(input))
|
601
|
+
modifying! # short-circuit before parsing the string
|
435
602
|
tuples = str_to_tuples str
|
436
603
|
@tuples, @string = [], -str
|
437
604
|
tuples_add tuples
|
605
|
+
else
|
606
|
+
raise ArgumentError, "expected a string or nil, got #{input.class}"
|
438
607
|
end
|
608
|
+
str
|
439
609
|
end
|
440
610
|
|
441
611
|
# Returns the \IMAP +sequence-set+ string representation, or an empty
|
442
612
|
# string when the set is empty. Note that an empty set is invalid in the
|
443
613
|
# \IMAP syntax.
|
444
614
|
#
|
445
|
-
# Related: #valid_string, #normalized_string, #
|
615
|
+
# Related: #string, #valid_string, #normalized_string, #inspect
|
446
616
|
def to_s; string || "" end
|
447
617
|
|
448
618
|
# Freezes and returns the set. A frozen SequenceSet is Ractor-safe.
|
@@ -495,8 +665,9 @@ module Net
|
|
495
665
|
|
496
666
|
# :call-seq: self === other -> true | false | nil
|
497
667
|
#
|
498
|
-
# Returns whether +other+ is contained within the set.
|
499
|
-
#
|
668
|
+
# Returns whether +other+ is contained within the set. +other+ may be any
|
669
|
+
# object that would be accepted by ::new. Returns +nil+ if StandardError
|
670
|
+
# is raised while converting +other+ to a comparable type.
|
500
671
|
#
|
501
672
|
# Related: #cover?, #include?, #include_star?
|
502
673
|
def ===(other)
|
@@ -510,12 +681,12 @@ module Net
|
|
510
681
|
# Returns whether +other+ is contained within the set. +other+ may be any
|
511
682
|
# object that would be accepted by ::new.
|
512
683
|
#
|
513
|
-
# Related: #===, #include?, #include_star?
|
684
|
+
# Related: #===, #include?, #include_star?, #intersect?
|
514
685
|
def cover?(other) input_to_tuples(other).none? { !include_tuple?(_1) } end
|
515
686
|
|
516
687
|
# Returns +true+ when a given number or range is in +self+, and +false+
|
517
|
-
# otherwise. Returns +
|
518
|
-
# <tt>*</tt
|
688
|
+
# otherwise. Returns +nil+ when +number+ isn't a valid SequenceSet
|
689
|
+
# element (Integer, Range, <tt>*</tt>, +sequence-set+ string).
|
519
690
|
#
|
520
691
|
# set = Net::IMAP::SequenceSet["5:10,100,111:115"]
|
521
692
|
# set.include? 1 #=> false
|
@@ -523,8 +694,8 @@ module Net
|
|
523
694
|
# set.include? 11..20 #=> false
|
524
695
|
# set.include? 100 #=> true
|
525
696
|
# set.include? 6 #=> true, covered by "5:10"
|
526
|
-
# set.include?
|
527
|
-
# set.include? "
|
697
|
+
# set.include? 6..9 #=> true, covered by "5:10"
|
698
|
+
# set.include? "6:9" #=> true, strings are parsed
|
528
699
|
# set.include? 4..9 #=> false, intersection is not sufficient
|
529
700
|
# set.include? "*" #=> false, use #limit to re-interpret "*"
|
530
701
|
# set.include? -1 #=> false, -1 is interpreted as "*"
|
@@ -533,11 +704,14 @@ module Net
|
|
533
704
|
# set.include? :* #=> true
|
534
705
|
# set.include? "*" #=> true
|
535
706
|
# set.include? -1 #=> true
|
536
|
-
# set.include?
|
537
|
-
# set.include?
|
707
|
+
# set.include?(200..) #=> true
|
708
|
+
# set.include?(100..) #=> false
|
538
709
|
#
|
539
|
-
# Related: #include_star?, #cover?,
|
540
|
-
def include?(element)
|
710
|
+
# Related: #include_star?, #cover?, #===, #intersect?
|
711
|
+
def include?(element)
|
712
|
+
tuple = input_to_tuple element rescue nil
|
713
|
+
!!include_tuple?(tuple) if tuple
|
714
|
+
end
|
541
715
|
|
542
716
|
alias member? include?
|
543
717
|
|
@@ -550,7 +724,7 @@ module Net
|
|
550
724
|
# Net::IMAP::SequenceSet["5:10"].intersect? "7,9,11" #=> true
|
551
725
|
# Net::IMAP::SequenceSet["5:10"].intersect? "11:33" #=> false
|
552
726
|
#
|
553
|
-
# Related: #intersection, #disjoint?
|
727
|
+
# Related: #intersection, #disjoint?, #cover?, #include?
|
554
728
|
def intersect?(other)
|
555
729
|
valid? && input_to_tuples(other).any? { intersect_tuple? _1 }
|
556
730
|
end
|
@@ -567,26 +741,53 @@ module Net
|
|
567
741
|
empty? || input_to_tuples(other).none? { intersect_tuple? _1 }
|
568
742
|
end
|
569
743
|
|
570
|
-
# :call-seq:
|
744
|
+
# :call-seq:
|
745
|
+
# max(star: :*) => integer or star or nil
|
746
|
+
# max(count) => SequenceSet
|
571
747
|
#
|
572
748
|
# Returns the maximum value in +self+, +star+ when the set includes
|
573
749
|
# <tt>*</tt>, or +nil+ when the set is empty.
|
574
|
-
|
575
|
-
|
750
|
+
#
|
751
|
+
# When +count+ is given, a new SequenceSet is returned, containing only
|
752
|
+
# the last +count+ numbers. An empty SequenceSet is returned when +self+
|
753
|
+
# is empty. (+star+ is ignored when +count+ is given.)
|
754
|
+
#
|
755
|
+
# Related: #min, #minmax, #slice
|
756
|
+
def max(count = nil, star: :*)
|
757
|
+
if count
|
758
|
+
slice(-[count, size].min..) || remain_frozen_empty
|
759
|
+
elsif (val = @tuples.last&.last)
|
760
|
+
val == STAR_INT ? star : val
|
761
|
+
end
|
576
762
|
end
|
577
763
|
|
578
|
-
# :call-seq:
|
764
|
+
# :call-seq:
|
765
|
+
# min(star: :*) => integer or star or nil
|
766
|
+
# min(count) => SequenceSet
|
579
767
|
#
|
580
768
|
# Returns the minimum value in +self+, +star+ when the only value in the
|
581
769
|
# set is <tt>*</tt>, or +nil+ when the set is empty.
|
582
|
-
|
583
|
-
|
770
|
+
#
|
771
|
+
# When +count+ is given, a new SequenceSet is returned, containing only
|
772
|
+
# the first +count+ numbers. An empty SequenceSet is returned when +self+
|
773
|
+
# is empty. (+star+ is ignored when +count+ is given.)
|
774
|
+
#
|
775
|
+
# Related: #max, #minmax, #slice
|
776
|
+
def min(count = nil, star: :*)
|
777
|
+
if count
|
778
|
+
slice(0...count) || remain_frozen_empty
|
779
|
+
elsif (val = @tuples.first&.first)
|
780
|
+
val != STAR_INT ? val : star
|
781
|
+
end
|
584
782
|
end
|
585
783
|
|
586
|
-
# :call-seq: minmax(star: :*) =>
|
784
|
+
# :call-seq: minmax(star: :*) => [min, max] or nil
|
587
785
|
#
|
588
786
|
# Returns a 2-element array containing the minimum and maximum numbers in
|
589
|
-
# +self+, or +nil+ when the set is empty.
|
787
|
+
# +self+, or +nil+ when the set is empty. +star+ is handled the same way
|
788
|
+
# as by #min and #max.
|
789
|
+
#
|
790
|
+
# Related: #min, #max
|
590
791
|
def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end
|
591
792
|
|
592
793
|
# Returns false when the set is empty.
|
@@ -606,14 +807,19 @@ module Net
|
|
606
807
|
# Returns a new sequence set that has every number in the +other+ object
|
607
808
|
# added.
|
608
809
|
#
|
609
|
-
# +other+ may be any object that would be accepted by ::new
|
610
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
611
|
-
# another sequence set, or an enumerable containing any of these.
|
810
|
+
# +other+ may be any object that would be accepted by ::new.
|
612
811
|
#
|
613
812
|
# Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
|
614
813
|
# #=> Net::IMAP::SequenceSet["1:6,99"]
|
615
814
|
#
|
616
|
-
# Related: #add, #merge
|
815
|
+
# Related: #add, #merge, #&, #-, #^, #~
|
816
|
+
#
|
817
|
+
# ==== Set identities
|
818
|
+
#
|
819
|
+
# <tt>lhs | rhs</tt> is equivalent to:
|
820
|
+
# * <tt>rhs | lhs</tt> (commutative)
|
821
|
+
# * <tt>~(~lhs & ~rhs)</tt> (De Morgan's Law)
|
822
|
+
# * <tt>(lhs & rhs) ^ (lhs ^ rhs)</tt>
|
617
823
|
def |(other) remain_frozen dup.merge other end
|
618
824
|
alias :+ :|
|
619
825
|
alias union :|
|
@@ -625,14 +831,22 @@ module Net
|
|
625
831
|
# Returns a new sequence set built by duplicating this set and removing
|
626
832
|
# every number that appears in +other+.
|
627
833
|
#
|
628
|
-
# +other+ may be any object that would be accepted by ::new
|
629
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
630
|
-
# another sequence set, or an enumerable containing any of these.
|
834
|
+
# +other+ may be any object that would be accepted by ::new.
|
631
835
|
#
|
632
836
|
# Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
|
633
837
|
# #=> Net::IMAP::SequenceSet["1,3,5"]
|
634
838
|
#
|
635
|
-
# Related: #subtract
|
839
|
+
# Related: #subtract, #|, #&, #^, #~
|
840
|
+
#
|
841
|
+
# ==== Set identities
|
842
|
+
#
|
843
|
+
# <tt>lhs - rhs</tt> is equivalent to:
|
844
|
+
# * <tt>~rhs - ~lhs</tt>
|
845
|
+
# * <tt>lhs & ~rhs</tt>
|
846
|
+
# * <tt>~(~lhs | rhs)</tt>
|
847
|
+
# * <tt>lhs & (lhs ^ rhs)</tt>
|
848
|
+
# * <tt>lhs ^ (lhs & rhs)</tt>
|
849
|
+
# * <tt>rhs ^ (lhs | rhs)</tt>
|
636
850
|
def -(other) remain_frozen dup.subtract other end
|
637
851
|
alias difference :-
|
638
852
|
|
@@ -643,14 +857,22 @@ module Net
|
|
643
857
|
# Returns a new sequence set containing only the numbers common to this
|
644
858
|
# set and +other+.
|
645
859
|
#
|
646
|
-
# +other+ may be any object that would be accepted by ::new
|
647
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
648
|
-
# another sequence set, or an enumerable containing any of these.
|
860
|
+
# +other+ may be any object that would be accepted by ::new.
|
649
861
|
#
|
650
862
|
# Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
|
651
863
|
# #=> Net::IMAP::SequenceSet["2,4"]
|
652
864
|
#
|
653
|
-
#
|
865
|
+
# Related: #intersect?, #|, #-, #^, #~
|
866
|
+
#
|
867
|
+
# ==== Set identities
|
868
|
+
#
|
869
|
+
# <tt>lhs & rhs</tt> is equivalent to:
|
870
|
+
# * <tt>rhs & lhs</tt> (commutative)
|
871
|
+
# * <tt>~(~lhs | ~rhs)</tt> (De Morgan's Law)
|
872
|
+
# * <tt>lhs - ~rhs</tt>
|
873
|
+
# * <tt>lhs - (lhs - rhs)</tt>
|
874
|
+
# * <tt>lhs - (lhs ^ rhs)</tt>
|
875
|
+
# * <tt>lhs ^ (lhs - rhs)</tt>
|
654
876
|
def &(other)
|
655
877
|
remain_frozen dup.subtract SequenceSet.new(other).complement!
|
656
878
|
end
|
@@ -663,16 +885,22 @@ module Net
|
|
663
885
|
# Returns a new sequence set containing numbers that are exclusive between
|
664
886
|
# this set and +other+.
|
665
887
|
#
|
666
|
-
# +other+ may be any object that would be accepted by ::new
|
667
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
668
|
-
# another sequence set, or an enumerable containing any of these.
|
888
|
+
# +other+ may be any object that would be accepted by ::new.
|
669
889
|
#
|
670
890
|
# Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
|
671
891
|
# #=> Net::IMAP::SequenceSet["1,3,5:6"]
|
672
892
|
#
|
673
|
-
#
|
674
|
-
#
|
675
|
-
|
893
|
+
# Related: #|, #&, #-, #~
|
894
|
+
#
|
895
|
+
# ==== Set identities
|
896
|
+
#
|
897
|
+
# <tt>lhs ^ rhs</tt> is equivalent to:
|
898
|
+
# * <tt>rhs ^ lhs</tt> (commutative)
|
899
|
+
# * <tt>~lhs ^ ~rhs</tt>
|
900
|
+
# * <tt>(lhs | rhs) - (lhs & rhs)</tt>
|
901
|
+
# * <tt>(lhs - rhs) | (rhs - lhs)</tt>
|
902
|
+
# * <tt>(lhs ^ other) ^ (other ^ rhs)</tt>
|
903
|
+
def ^(other) remain_frozen (dup | other).subtract(self & other) end
|
676
904
|
alias xor :^
|
677
905
|
|
678
906
|
# :call-seq:
|
@@ -689,7 +917,12 @@ module Net
|
|
689
917
|
# ~Net::IMAP::SequenceSet["6:99,223:*"]
|
690
918
|
# #=> Net::IMAP::SequenceSet["1:5,100:222"]
|
691
919
|
#
|
692
|
-
# Related: #complement
|
920
|
+
# Related: #complement!, #|, #&, #-, #^
|
921
|
+
#
|
922
|
+
# ==== Set identities
|
923
|
+
#
|
924
|
+
# <tt>~set</tt> is equivalent to:
|
925
|
+
# * <tt>full - set</tt>, where "full" is Net::IMAP::SequenceSet.full
|
693
926
|
def ~; remain_frozen dup.complement! end
|
694
927
|
alias complement :~
|
695
928
|
|
@@ -701,8 +934,12 @@ module Net
|
|
701
934
|
#
|
702
935
|
# #string will be regenerated. Use #merge to add many elements at once.
|
703
936
|
#
|
704
|
-
#
|
937
|
+
# Use #append to append new elements to #string. See
|
938
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
939
|
+
#
|
940
|
+
# Related: #add?, #merge, #union, #append
|
705
941
|
def add(element)
|
942
|
+
modifying! # short-circuit before input_to_tuple
|
706
943
|
tuple_add input_to_tuple element
|
707
944
|
normalize!
|
708
945
|
end
|
@@ -712,8 +949,12 @@ module Net
|
|
712
949
|
#
|
713
950
|
# Unlike #add, #merge, or #union, the new value is appended to #string.
|
714
951
|
# This may result in a #string which has duplicates or is out-of-order.
|
952
|
+
#
|
953
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
954
|
+
#
|
955
|
+
# Related: #add, #merge, #union
|
715
956
|
def append(entry)
|
716
|
-
modifying!
|
957
|
+
modifying! # short-circuit before input_to_tuple
|
717
958
|
tuple = input_to_tuple entry
|
718
959
|
entry = tuple_to_str tuple
|
719
960
|
string unless empty? # write @string before tuple_add
|
@@ -731,6 +972,7 @@ module Net
|
|
731
972
|
#
|
732
973
|
# Related: #add, #merge, #union, #include?
|
733
974
|
def add?(element)
|
975
|
+
modifying! # short-circuit before include?
|
734
976
|
add element unless include? element
|
735
977
|
end
|
736
978
|
|
@@ -743,6 +985,7 @@ module Net
|
|
743
985
|
#
|
744
986
|
# Related: #delete?, #delete_at, #subtract, #difference
|
745
987
|
def delete(element)
|
988
|
+
modifying! # short-circuit before input_to_tuple
|
746
989
|
tuple_subtract input_to_tuple element
|
747
990
|
normalize!
|
748
991
|
end
|
@@ -780,6 +1023,7 @@ module Net
|
|
780
1023
|
#
|
781
1024
|
# Related: #delete, #delete_at, #subtract, #difference, #disjoint?
|
782
1025
|
def delete?(element)
|
1026
|
+
modifying! # short-circuit before input_to_tuple
|
783
1027
|
tuple = input_to_tuple element
|
784
1028
|
if tuple.first == tuple.last
|
785
1029
|
return unless include_tuple? tuple
|
@@ -820,6 +1064,7 @@ module Net
|
|
820
1064
|
#
|
821
1065
|
# Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference
|
822
1066
|
def slice!(index, length = nil)
|
1067
|
+
modifying! # short-circuit before slice
|
823
1068
|
deleted = slice(index, length) and subtract deleted
|
824
1069
|
deleted
|
825
1070
|
end
|
@@ -827,14 +1072,13 @@ module Net
|
|
827
1072
|
# Merges all of the elements that appear in any of the +sets+ into the
|
828
1073
|
# set, and returns +self+.
|
829
1074
|
#
|
830
|
-
# The +sets+ may be any objects that would be accepted by ::new
|
831
|
-
# 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
|
832
|
-
# strings, other sequence sets, or enumerables containing any of these.
|
1075
|
+
# The +sets+ may be any objects that would be accepted by ::new.
|
833
1076
|
#
|
834
1077
|
# #string will be regenerated after all sets have been merged.
|
835
1078
|
#
|
836
1079
|
# Related: #add, #add?, #union
|
837
1080
|
def merge(*sets)
|
1081
|
+
modifying! # short-circuit before input_to_tuples
|
838
1082
|
tuples_add input_to_tuples sets
|
839
1083
|
normalize!
|
840
1084
|
end
|
@@ -842,9 +1086,7 @@ module Net
|
|
842
1086
|
# Removes all of the elements that appear in any of the given +sets+ from
|
843
1087
|
# the set, and returns +self+.
|
844
1088
|
#
|
845
|
-
# The +sets+ may be any objects that would be accepted by ::new
|
846
|
-
# 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
|
847
|
-
# strings, other sequence sets, or enumerables containing any of these.
|
1089
|
+
# The +sets+ may be any objects that would be accepted by ::new.
|
848
1090
|
#
|
849
1091
|
# Related: #difference
|
850
1092
|
def subtract(*sets)
|
@@ -860,21 +1102,21 @@ module Net
|
|
860
1102
|
# This is useful when the given order is significant, for example in a
|
861
1103
|
# ESEARCH response to IMAP#sort.
|
862
1104
|
#
|
1105
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
1106
|
+
#
|
863
1107
|
# Related: #each_entry, #elements
|
864
1108
|
def entries; each_entry.to_a end
|
865
1109
|
|
866
1110
|
# Returns an array of ranges and integers and <tt>:*</tt>.
|
867
1111
|
#
|
868
1112
|
# The returned elements are sorted and coalesced, even when the input
|
869
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
1113
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
1114
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
870
1115
|
#
|
871
1116
|
# By itself, <tt>*</tt> translates to <tt>:*</tt>. A range containing
|
872
1117
|
# <tt>*</tt> translates to an endless range. Use #limit to translate both
|
873
1118
|
# cases to a maximum value.
|
874
1119
|
#
|
875
|
-
# The returned elements will be sorted and coalesced, even when the input
|
876
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize.
|
877
|
-
#
|
878
1120
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
|
879
1121
|
# #=> [2, 5..9, 11..12, :*]
|
880
1122
|
#
|
@@ -885,15 +1127,13 @@ module Net
|
|
885
1127
|
# Returns an array of ranges
|
886
1128
|
#
|
887
1129
|
# The returned elements are sorted and coalesced, even when the input
|
888
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
1130
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
1131
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
889
1132
|
#
|
890
1133
|
# <tt>*</tt> translates to an endless range. By itself, <tt>*</tt>
|
891
1134
|
# translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
|
892
1135
|
# value.
|
893
1136
|
#
|
894
|
-
# The returned ranges will be sorted and coalesced, even when the input
|
895
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize.
|
896
|
-
#
|
897
1137
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
|
898
1138
|
# #=> [2..2, 5..9, 11..12, :*..]
|
899
1139
|
# Net::IMAP::SequenceSet["123,999:*,456:789"].ranges
|
@@ -905,7 +1145,7 @@ module Net
|
|
905
1145
|
# Returns a sorted array of all of the number values in the sequence set.
|
906
1146
|
#
|
907
1147
|
# The returned numbers are sorted and de-duplicated, even when the input
|
908
|
-
# #string is not. See #normalize.
|
1148
|
+
# #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
|
909
1149
|
#
|
910
1150
|
# Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
|
911
1151
|
# #=> [2, 5, 6, 7, 8, 9, 11, 12]
|
@@ -937,6 +1177,8 @@ module Net
|
|
937
1177
|
# no sorting, deduplication, or coalescing. When #string is in its
|
938
1178
|
# normalized form, this will yield the same values as #each_element.
|
939
1179
|
#
|
1180
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
1181
|
+
#
|
940
1182
|
# Related: #entries, #each_element
|
941
1183
|
def each_entry(&block) # :yields: integer or range or :*
|
942
1184
|
return to_enum(__method__) unless block_given?
|
@@ -947,7 +1189,7 @@ module Net
|
|
947
1189
|
# and returns self. Returns an enumerator when called without a block.
|
948
1190
|
#
|
949
1191
|
# The returned numbers are sorted and de-duplicated, even when the input
|
950
|
-
# #string is not. See #normalize.
|
1192
|
+
# #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
|
951
1193
|
#
|
952
1194
|
# Related: #elements, #each_entry
|
953
1195
|
def each_element # :yields: integer or range or :*
|
@@ -1241,20 +1483,76 @@ module Net
|
|
1241
1483
|
def slice_range(range)
|
1242
1484
|
first = range.begin || 0
|
1243
1485
|
last = range.end || -1
|
1244
|
-
|
1486
|
+
if range.exclude_end?
|
1487
|
+
return remain_frozen_empty if last.zero?
|
1488
|
+
last -= 1 if range.end && last != STAR_INT
|
1489
|
+
end
|
1245
1490
|
if (first * last).positive? && last < first
|
1246
|
-
|
1491
|
+
remain_frozen_empty
|
1247
1492
|
elsif (min = at(first))
|
1248
1493
|
max = at(last)
|
1494
|
+
max = :* if max.nil?
|
1249
1495
|
if max == :* then self & (min..)
|
1250
1496
|
elsif min <= max then self & (min..max)
|
1251
|
-
else
|
1497
|
+
else remain_frozen_empty
|
1252
1498
|
end
|
1253
1499
|
end
|
1254
1500
|
end
|
1255
1501
|
|
1256
1502
|
public
|
1257
1503
|
|
1504
|
+
# Returns a copy of +self+ which only contains the numbers above +num+.
|
1505
|
+
#
|
1506
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
|
1507
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(20) # to_s => "21:22,50
|
1508
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(30) # to_s => "50"
|
1509
|
+
#
|
1510
|
+
# This returns the same result as #intersection with <tt>((num+1)..)</tt>
|
1511
|
+
# or #difference with <tt>(..num)</tt>.
|
1512
|
+
#
|
1513
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (11..) # to_s => "11:22,50"
|
1514
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..10) # to_s => "11:22,50"
|
1515
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (21..) # to_s => "21:22,50"
|
1516
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..20) # to_s => "21:22,50"
|
1517
|
+
#
|
1518
|
+
# Related: #above, #-, #&
|
1519
|
+
def above(num)
|
1520
|
+
NumValidator.valid_nz_number?(num) or
|
1521
|
+
raise ArgumentError, "not a valid sequence set number"
|
1522
|
+
difference(..num)
|
1523
|
+
end
|
1524
|
+
|
1525
|
+
# Returns a copy of +self+ which only contains numbers below +num+.
|
1526
|
+
#
|
1527
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(10) # to_s => "5"
|
1528
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(20) # to_s => "5,10:19"
|
1529
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
1530
|
+
#
|
1531
|
+
# This returns the same result as #intersection with <tt>(..(num-1))</tt>
|
1532
|
+
# or #difference with <tt>(num..)</tt>.
|
1533
|
+
#
|
1534
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..9) # to_s => "5"
|
1535
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (10..) # to_s => "5"
|
1536
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..19) # to_s => "5,10:19"
|
1537
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (20..) # to_s => "5,10:19"
|
1538
|
+
#
|
1539
|
+
# When the set does not contain <tt>*</tt>, #below is identical to #limit
|
1540
|
+
# with <tt>max: num - 1</tt>. When the set does contain <tt>*</tt>,
|
1541
|
+
# #below always drops it from the result. Use #limit when the IMAP
|
1542
|
+
# semantics for <tt>*</tt> must be enforced.
|
1543
|
+
#
|
1544
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
1545
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 29) # to_s => "5,10:22"
|
1546
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].below(30) # to_s => "5,10:22"
|
1547
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].limit(max: 29) # to_s => "5,10:22,29"
|
1548
|
+
#
|
1549
|
+
# Related: #above, #-, #&, #limit
|
1550
|
+
def below(num)
|
1551
|
+
NumValidator.valid_nz_number?(num) or
|
1552
|
+
raise ArgumentError, "not a valid sequence set number"
|
1553
|
+
difference(num..)
|
1554
|
+
end
|
1555
|
+
|
1258
1556
|
# Returns a frozen SequenceSet with <tt>*</tt> converted to +max+, numbers
|
1259
1557
|
# and ranges over +max+ removed, and ranges containing +max+ converted to
|
1260
1558
|
# end at +max+.
|
@@ -1272,6 +1570,7 @@ module Net
|
|
1272
1570
|
# Net::IMAP::SequenceSet["500:*"].limit(max: 37)
|
1273
1571
|
# #=> Net::IMAP::SequenceSet["37"]
|
1274
1572
|
#
|
1573
|
+
# Related: #limit!
|
1275
1574
|
def limit(max:)
|
1276
1575
|
max = to_tuple_int(max)
|
1277
1576
|
if empty? then self.class.empty
|
@@ -1286,6 +1585,7 @@ module Net
|
|
1286
1585
|
#
|
1287
1586
|
# Related: #limit
|
1288
1587
|
def limit!(max:)
|
1588
|
+
modifying! # short-circuit, and normalize the error message for JRuby
|
1289
1589
|
star = include_star?
|
1290
1590
|
max = to_tuple_int(max)
|
1291
1591
|
tuple_subtract [max + 1, STAR_INT]
|
@@ -1300,6 +1600,7 @@ module Net
|
|
1300
1600
|
#
|
1301
1601
|
# Related: #complement
|
1302
1602
|
def complement!
|
1603
|
+
modifying! # short-circuit, and normalize the error message for JRuby
|
1303
1604
|
return replace(self.class.full) if empty?
|
1304
1605
|
return clear if full?
|
1305
1606
|
flat = @tuples.flat_map { [_1 - 1, _2 + 1] }
|
@@ -1313,6 +1614,7 @@ module Net
|
|
1313
1614
|
#
|
1314
1615
|
# The returned set's #string is sorted and deduplicated. Adjacent or
|
1315
1616
|
# overlapping elements will be merged into a single larger range.
|
1617
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
1316
1618
|
#
|
1317
1619
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
|
1318
1620
|
# #=> Net::IMAP::SequenceSet["1:7,9:11"]
|
@@ -1325,37 +1627,87 @@ module Net
|
|
1325
1627
|
end
|
1326
1628
|
|
1327
1629
|
# Resets #string to be sorted, deduplicated, and coalesced. Returns
|
1328
|
-
# +self+.
|
1630
|
+
# +self+. See SequenceSet@Ordered+and+Normalized+sets.
|
1329
1631
|
#
|
1330
1632
|
# Related: #normalize, #normalized_string
|
1331
1633
|
def normalize!
|
1634
|
+
modifying! # redundant check, to normalize the error message for JRuby
|
1332
1635
|
@string = nil
|
1333
1636
|
self
|
1334
1637
|
end
|
1335
1638
|
|
1336
1639
|
# Returns a normalized +sequence-set+ string representation, sorted
|
1337
1640
|
# and deduplicated. Adjacent or overlapping elements will be merged into
|
1338
|
-
# a single larger range.
|
1641
|
+
# a single larger range. See SequenceSet@Ordered+and+Normalized+sets.
|
1339
1642
|
#
|
1340
1643
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
|
1341
1644
|
# #=> "1:7,9:11"
|
1342
1645
|
#
|
1343
|
-
#
|
1646
|
+
# Returns +nil+ when the set is empty.
|
1647
|
+
#
|
1648
|
+
# Related: #normalize!, #normalize, #string, #to_s
|
1344
1649
|
def normalized_string
|
1345
1650
|
@tuples.empty? ? nil : -@tuples.map { tuple_to_str _1 }.join(",")
|
1346
1651
|
end
|
1347
1652
|
|
1653
|
+
# Returns an inspection string for the SequenceSet.
|
1654
|
+
#
|
1655
|
+
# Net::IMAP::SequenceSet.new.inspect
|
1656
|
+
# #=> "Net::IMAP::SequenceSet()"
|
1657
|
+
#
|
1658
|
+
# Net::IMAP::SequenceSet(1..5, 1024, 15, 2000).inspect
|
1659
|
+
# #=> 'Net::IMAP::SequenceSet("1:5,15,1024,2000")'
|
1660
|
+
#
|
1661
|
+
# Frozen sets have slightly different output:
|
1662
|
+
#
|
1663
|
+
# Net::IMAP::SequenceSet.empty.inspect
|
1664
|
+
# #=> "Net::IMAP::SequenceSet.empty"
|
1665
|
+
#
|
1666
|
+
# Net::IMAP::SequenceSet[1..5, 1024, 15, 2000].inspect
|
1667
|
+
# #=> 'Net::IMAP::SequenceSet["1:5,15,1024,2000"]'
|
1668
|
+
#
|
1669
|
+
# Large sets (by number of #entries) have abridged output, with only the
|
1670
|
+
# first and last entries:
|
1671
|
+
#
|
1672
|
+
# Net::IMAP::SequenceSet(((1..5000) % 2).to_a).inspect
|
1673
|
+
# #=> #<Net::IMAP::SequenceSet 2500 entries "1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,...(2468 entries omitted)...,4969,4971,4973,4975,4977,4979,4981,4983,4985,4987,4989,4991,4993,4995,4997,4999">
|
1674
|
+
#
|
1675
|
+
# Related: #to_s, #string
|
1348
1676
|
def inspect
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1677
|
+
case (count = count_entries)
|
1678
|
+
when 0
|
1679
|
+
(frozen? ? "%s.empty" : "%s()") % [self.class]
|
1680
|
+
when ..INSPECT_MAX_LEN
|
1681
|
+
(frozen? ? "%s[%p]" : "%s(%p)") % [self.class, to_s]
|
1353
1682
|
else
|
1354
|
-
|
1683
|
+
if @string
|
1684
|
+
head = @string[INSPECT_ABRIDGED_HEAD_RE]
|
1685
|
+
tail = @string[INSPECT_ABRIDGED_TAIL_RE]
|
1686
|
+
else
|
1687
|
+
head = export_string_entries(@tuples.first(INSPECT_TRUNCATE_LEN)) + ","
|
1688
|
+
tail = "," + export_string_entries(@tuples.last(INSPECT_TRUNCATE_LEN))
|
1689
|
+
end
|
1690
|
+
'#<%s %d entries "%s...(%d entries omitted)...%s"%s>' % [
|
1691
|
+
self.class, count,
|
1692
|
+
head, count - INSPECT_TRUNCATE_LEN * 2, tail,
|
1693
|
+
frozen? ? " (frozen)" : "",
|
1694
|
+
]
|
1355
1695
|
end
|
1356
1696
|
end
|
1357
1697
|
|
1358
|
-
|
1698
|
+
private def count_entries
|
1699
|
+
@string ? @string.count(",") + 1 : @tuples.count
|
1700
|
+
end
|
1701
|
+
|
1702
|
+
##
|
1703
|
+
# :method: to_sequence_set
|
1704
|
+
# :call-seq: to_sequence_set -> self
|
1705
|
+
#
|
1706
|
+
# Returns +self+
|
1707
|
+
#
|
1708
|
+
# Related: ::try_convert
|
1709
|
+
|
1710
|
+
# :nodoc: (work around rdoc bug)
|
1359
1711
|
alias to_sequence_set itself
|
1360
1712
|
|
1361
1713
|
# Unstable API: currently for internal use only (Net::IMAP#validate_data)
|
@@ -1385,18 +1737,21 @@ module Net
|
|
1385
1737
|
|
1386
1738
|
attr_reader :tuples # :nodoc:
|
1387
1739
|
|
1740
|
+
def deep_copy_tuples; @tuples.map { _1.dup } end # :nodoc:
|
1741
|
+
|
1388
1742
|
private
|
1389
1743
|
|
1390
1744
|
def remain_frozen(set) frozen? ? set.freeze : set end
|
1745
|
+
def remain_frozen_empty; frozen? ? SequenceSet.empty : SequenceSet.new end
|
1391
1746
|
|
1392
1747
|
# frozen clones are shallow copied
|
1393
1748
|
def initialize_clone(other)
|
1394
|
-
other.
|
1749
|
+
@tuples = other.deep_copy_tuples unless other.frozen?
|
1750
|
+
super
|
1395
1751
|
end
|
1396
1752
|
|
1397
1753
|
def initialize_dup(other)
|
1398
|
-
@tuples = other.
|
1399
|
-
@string = other.string&.-@
|
1754
|
+
@tuples = other.deep_copy_tuples
|
1400
1755
|
super
|
1401
1756
|
end
|
1402
1757
|
|
@@ -1421,9 +1776,8 @@ module Net
|
|
1421
1776
|
when Array then set.flat_map { input_to_tuples _1 }
|
1422
1777
|
when nil then []
|
1423
1778
|
else
|
1424
|
-
raise DataFormatError,
|
1425
|
-
|
1426
|
-
"got %p" % [set]
|
1779
|
+
raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
|
1780
|
+
"got %p" % [set]
|
1427
1781
|
end
|
1428
1782
|
end
|
1429
1783
|
|
@@ -1449,6 +1803,10 @@ module Net
|
|
1449
1803
|
def to_tuple_int(obj) STARS.include?(obj) ? STAR_INT : nz_number(obj) end
|
1450
1804
|
def from_tuple_int(num) num == STAR_INT ? :* : num end
|
1451
1805
|
|
1806
|
+
def export_string_entries(entries)
|
1807
|
+
-entries.map { tuple_to_str _1 }.join(",")
|
1808
|
+
end
|
1809
|
+
|
1452
1810
|
def tuple_to_str(tuple) tuple.uniq.map{ from_tuple_int _1 }.join(":") end
|
1453
1811
|
def str_to_tuples(str) str.split(",", -1).map! { str_to_tuple _1 } end
|
1454
1812
|
def str_to_tuple(str)
|
data/lib/net/imap.rb
CHANGED
@@ -788,7 +788,7 @@ module Net
|
|
788
788
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
789
789
|
#
|
790
790
|
class IMAP < Protocol
|
791
|
-
VERSION = "0.5.
|
791
|
+
VERSION = "0.5.10"
|
792
792
|
|
793
793
|
# Aliases for supported capabilities, to be used with the #enable command.
|
794
794
|
ENABLE_ALIASES = {
|
@@ -801,6 +801,7 @@ module Net
|
|
801
801
|
autoload :ResponseReader, "#{dir}/response_reader"
|
802
802
|
autoload :SASL, "#{dir}/sasl"
|
803
803
|
autoload :SASLAdapter, "#{dir}/sasl_adapter"
|
804
|
+
autoload :SequenceSet, "#{dir}/sequence_set"
|
804
805
|
autoload :StringPrep, "#{dir}/stringprep"
|
805
806
|
|
806
807
|
include MonitorMixin
|
@@ -809,6 +810,22 @@ module Net
|
|
809
810
|
include SSL
|
810
811
|
end
|
811
812
|
|
813
|
+
# :call-seq:
|
814
|
+
# Net::IMAP::SequenceSet(set = nil) -> SequenceSet
|
815
|
+
#
|
816
|
+
# Coerces +set+ into a SequenceSet, using either SequenceSet.try_convert or
|
817
|
+
# SequenceSet.new.
|
818
|
+
#
|
819
|
+
# * When +set+ is a SequenceSet, that same set is returned.
|
820
|
+
# * When +set+ responds to +to_sequence_set+, +set.to_sequence_set+ is
|
821
|
+
# returned.
|
822
|
+
# * Otherwise, returns the result from calling SequenceSet.new with +set+.
|
823
|
+
#
|
824
|
+
# Related: SequenceSet.try_convert, SequenceSet.new, SequenceSet::[]
|
825
|
+
def self.SequenceSet(set = nil)
|
826
|
+
SequenceSet.try_convert(set) || SequenceSet.new(set)
|
827
|
+
end
|
828
|
+
|
812
829
|
# Returns the global Config object
|
813
830
|
def self.config; Config.global end
|
814
831
|
|
@@ -1114,28 +1131,27 @@ module Net
|
|
1114
1131
|
|
1115
1132
|
# Disconnects from the server.
|
1116
1133
|
#
|
1134
|
+
# Waits for receiver thread to close before returning. Slow or stuck
|
1135
|
+
# response handlers can cause #disconnect to hang until they complete.
|
1136
|
+
#
|
1117
1137
|
# Related: #logout, #logout!
|
1118
1138
|
def disconnect
|
1139
|
+
in_logout_state = try_state_logout?
|
1119
1140
|
return if disconnected?
|
1120
|
-
state_logout!
|
1121
1141
|
begin
|
1122
|
-
|
1123
|
-
# try to call SSL::SSLSocket#io.
|
1124
|
-
@sock.io.shutdown
|
1125
|
-
rescue NoMethodError
|
1126
|
-
# @sock is not an SSL::SSLSocket.
|
1127
|
-
@sock.shutdown
|
1128
|
-
end
|
1142
|
+
@sock.to_io.shutdown
|
1129
1143
|
rescue Errno::ENOTCONN
|
1130
1144
|
# ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
|
1131
1145
|
rescue Exception => e
|
1132
1146
|
@receiver_thread.raise(e)
|
1133
1147
|
end
|
1148
|
+
@sock.close
|
1134
1149
|
@receiver_thread.join
|
1135
|
-
synchronize do
|
1136
|
-
@sock.close
|
1137
|
-
end
|
1138
1150
|
raise e if e
|
1151
|
+
ensure
|
1152
|
+
# Try again after shutting down the receiver thread. With no reciever
|
1153
|
+
# left to wait for, any remaining locks should be _very_ brief.
|
1154
|
+
state_logout! unless in_logout_state
|
1139
1155
|
end
|
1140
1156
|
|
1141
1157
|
# Returns true if disconnected from the server.
|
@@ -3062,8 +3078,8 @@ module Net
|
|
3062
3078
|
raise @exception || Net::IMAP::Error.new("connection closed")
|
3063
3079
|
end
|
3064
3080
|
ensure
|
3081
|
+
remove_response_handler(response_handler)
|
3065
3082
|
unless @receiver_thread_terminating
|
3066
|
-
remove_response_handler(response_handler)
|
3067
3083
|
put_string("DONE#{CRLF}")
|
3068
3084
|
response = get_tagged_response(tag, "IDLE", idle_response_timeout)
|
3069
3085
|
end
|
@@ -3346,8 +3362,6 @@ module Net
|
|
3346
3362
|
rescue Exception => ex
|
3347
3363
|
@receiver_thread_exception = ex
|
3348
3364
|
# don't exit the thread with an exception
|
3349
|
-
ensure
|
3350
|
-
state_logout!
|
3351
3365
|
end
|
3352
3366
|
end
|
3353
3367
|
|
@@ -3429,6 +3443,8 @@ module Net
|
|
3429
3443
|
@idle_done_cond.signal
|
3430
3444
|
end
|
3431
3445
|
end
|
3446
|
+
ensure
|
3447
|
+
state_logout!
|
3432
3448
|
end
|
3433
3449
|
|
3434
3450
|
def get_tagged_response(tag, cmd, timeout = nil)
|
@@ -3791,15 +3807,29 @@ module Net
|
|
3791
3807
|
end
|
3792
3808
|
|
3793
3809
|
def state_unselected!
|
3794
|
-
|
3810
|
+
synchronize do
|
3811
|
+
state_authenticated! if connection_state.to_sym == :selected
|
3812
|
+
end
|
3795
3813
|
end
|
3796
3814
|
|
3797
3815
|
def state_logout!
|
3816
|
+
return true if connection_state in [:logout, *]
|
3798
3817
|
synchronize do
|
3818
|
+
return true if connection_state in [:logout, *]
|
3799
3819
|
@connection_state = ConnectionState::Logout.new
|
3800
3820
|
end
|
3801
3821
|
end
|
3802
3822
|
|
3823
|
+
# don't wait to aqcuire the lock
|
3824
|
+
def try_state_logout?
|
3825
|
+
return true if connection_state in [:logout, *]
|
3826
|
+
return false unless acquired_lock = mon_try_enter
|
3827
|
+
state_logout!
|
3828
|
+
true
|
3829
|
+
ensure
|
3830
|
+
mon_exit if acquired_lock
|
3831
|
+
end
|
3832
|
+
|
3803
3833
|
def sasl_adapter
|
3804
3834
|
SASLAdapter.new(self, &method(:send_command_with_continuations))
|
3805
3835
|
end
|
@@ -388,9 +388,11 @@ class StringPrepTablesGenerator
|
|
388
388
|
end
|
389
389
|
|
390
390
|
def asgn_mapping(name, replacement = to_map(tables[name]))
|
391
|
+
indent = " " * 2
|
392
|
+
replacement = replacement.inspect.gsub(/" => "/, '"=>"')
|
391
393
|
cname = name.tr(?., ?_).upcase
|
392
|
-
"# Replacements for %s\n%s%s = %
|
393
|
-
"IN_#{name}",
|
394
|
+
"# Replacements for %s\n%s%s = %s.freeze" % [
|
395
|
+
"IN_#{name}", indent, "MAP_#{cname}", replacement,
|
394
396
|
]
|
395
397
|
end
|
396
398
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-imap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shugo Maeda
|
@@ -129,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
129
|
- !ruby/object:Gem::Version
|
130
130
|
version: '0'
|
131
131
|
requirements: []
|
132
|
-
rubygems_version: 3.6.
|
132
|
+
rubygems_version: 3.6.9
|
133
133
|
specification_version: 4
|
134
134
|
summary: Ruby client api for Internet Message Access Protocol
|
135
135
|
test_files: []
|