net-imap 0.4.24 → 0.5.14
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 +10 -1
- data/README.md +10 -4
- data/docs/styles.css +75 -14
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +73 -78
- data/lib/net/imap/config/attr_type_coercion.rb +22 -10
- data/lib/net/imap/config/attr_version_defaults.rb +93 -0
- data/lib/net/imap/config.rb +70 -94
- data/lib/net/imap/connection_state.rb +48 -0
- data/lib/net/imap/data_encoding.rb +3 -3
- data/lib/net/imap/data_lite.rb +226 -0
- data/lib/net/imap/deprecated_client_options.rb +6 -3
- data/lib/net/imap/errors.rb +6 -0
- data/lib/net/imap/esearch_result.rb +219 -0
- data/lib/net/imap/fetch_data.rb +126 -47
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +120 -186
- data/lib/net/imap/response_parser/parser_utils.rb +5 -0
- data/lib/net/imap/response_parser.rb +155 -21
- data/lib/net/imap/response_reader.rb +9 -12
- data/lib/net/imap/sasl/anonymous_authenticator.rb +3 -3
- data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
- data/lib/net/imap/sasl/authenticators.rb +8 -4
- data/lib/net/imap/sasl/client_adapter.rb +77 -26
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +4 -4
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +218 -56
- data/lib/net/imap/sasl/external_authenticator.rb +2 -2
- data/lib/net/imap/sasl/gs2_header.rb +7 -7
- data/lib/net/imap/sasl/login_authenticator.rb +4 -3
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +6 -6
- data/lib/net/imap/sasl/plain_authenticator.rb +7 -7
- data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
- data/lib/net/imap/sasl/scram_authenticator.rb +10 -10
- data/lib/net/imap/sasl.rb +7 -4
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/search_result.rb +4 -5
- data/lib/net/imap/sequence_set.rb +529 -154
- data/lib/net/imap/stringprep/nameprep.rb +1 -1
- data/lib/net/imap/stringprep/trace.rb +4 -4
- data/lib/net/imap/uidplus_data.rb +2 -84
- data/lib/net/imap/vanished_data.rb +65 -0
- data/lib/net/imap.rb +996 -305
- data/net-imap.gemspec +1 -1
- data/rakelib/rfcs.rake +2 -0
- data/rakelib/string_prep_tables_generator.rb +6 -2
- metadata +7 -2
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "set" unless defined?(::Set)
|
|
4
|
+
|
|
3
5
|
module Net
|
|
4
6
|
class IMAP
|
|
5
7
|
|
|
@@ -14,30 +16,12 @@ module Net
|
|
|
14
16
|
# receive a SequenceSet as an argument, for example IMAP#search, IMAP#fetch,
|
|
15
17
|
# and IMAP#store.
|
|
16
18
|
#
|
|
17
|
-
# == EXPERIMENTAL API
|
|
18
|
-
#
|
|
19
|
-
# SequenceSet is currently experimental. Only two methods, ::[] and
|
|
20
|
-
# #valid_string, are considered stable. Although the API isn't expected to
|
|
21
|
-
# change much, any other methods may be removed or changed without
|
|
22
|
-
# deprecation.
|
|
23
|
-
#
|
|
24
19
|
# == Creating sequence sets
|
|
25
20
|
#
|
|
26
|
-
# SequenceSet.new with no arguments creates an empty sequence set. Note
|
|
27
|
-
# that an empty sequence set is invalid in the \IMAP grammar.
|
|
28
|
-
#
|
|
29
|
-
# set = Net::IMAP::SequenceSet.new
|
|
30
|
-
# set.empty? #=> true
|
|
31
|
-
# set.valid? #=> false
|
|
32
|
-
# set.valid_string #!> raises DataFormatError
|
|
33
|
-
# set << 1..10
|
|
34
|
-
# set.empty? #=> false
|
|
35
|
-
# set.valid? #=> true
|
|
36
|
-
# set.valid_string #=> "1:10"
|
|
37
|
-
#
|
|
38
21
|
# SequenceSet.new may receive a single optional argument: a non-zero 32 bit
|
|
39
22
|
# unsigned integer, a range, a <tt>sequence-set</tt> formatted string,
|
|
40
|
-
# another
|
|
23
|
+
# another SequenceSet, a Set (containing only numbers or <tt>*</tt>), or an
|
|
24
|
+
# Array containing any of these (array inputs may be nested).
|
|
41
25
|
#
|
|
42
26
|
# set = Net::IMAP::SequenceSet.new(1)
|
|
43
27
|
# set.valid_string #=> "1"
|
|
@@ -52,30 +36,114 @@ module Net
|
|
|
52
36
|
# set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
|
|
53
37
|
# set.valid_string #=> "1:10,55,1024:2048"
|
|
54
38
|
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
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"
|
|
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
|
|
57
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
|
|
58
104
|
# set = Net::IMAP::SequenceSet["1,2,3:7,5,6:10,2048,1024"]
|
|
59
105
|
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
|
|
106
|
+
# set.frozen? #=> true
|
|
107
|
+
#
|
|
108
|
+
# # Other inputs are normalized
|
|
60
109
|
# set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
|
|
61
|
-
# 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")
|
|
62
125
|
#
|
|
63
126
|
# == Ordered and Normalized sets
|
|
64
127
|
#
|
|
65
128
|
# Sometimes the order of the set's members is significant, such as with the
|
|
66
129
|
# +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
|
|
67
|
-
# sequence set is created
|
|
68
|
-
# #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.
|
|
69
135
|
#
|
|
70
|
-
# Internally, SequenceSet
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
# 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.
|
|
75
143
|
#
|
|
76
|
-
# Most modification methods
|
|
77
|
-
#
|
|
78
|
-
#
|
|
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.
|
|
79
147
|
#
|
|
80
148
|
# == Using <tt>*</tt>
|
|
81
149
|
#
|
|
@@ -112,11 +180,15 @@ module Net
|
|
|
112
180
|
# When a set includes <tt>*</tt>, some methods may have surprising behavior.
|
|
113
181
|
#
|
|
114
182
|
# For example, #complement treats <tt>*</tt> as its own number. This way,
|
|
115
|
-
# the #intersection of a set and its #complement will always be empty.
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
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:
|
|
120
192
|
#
|
|
121
193
|
# ~Net::IMAP::SequenceSet["*"] == Net::IMAP::SequenceSet[1..(2**32-1)]
|
|
122
194
|
# ~Net::IMAP::SequenceSet[1..5] == Net::IMAP::SequenceSet["6:*"]
|
|
@@ -153,6 +225,7 @@ module Net
|
|
|
153
225
|
# * ::new: Creates a new mutable sequence set, which may be empty (invalid).
|
|
154
226
|
# * ::try_convert: Calls +to_sequence_set+ on an object and verifies that
|
|
155
227
|
# the result is a SequenceSet.
|
|
228
|
+
# * Net::IMAP::SequenceSet(): Coerce an input using ::try_convert or ::new.
|
|
156
229
|
# * ::empty: Returns a frozen empty (invalid) SequenceSet.
|
|
157
230
|
# * ::full: Returns a frozen SequenceSet containing every possible number.
|
|
158
231
|
#
|
|
@@ -178,14 +251,13 @@ module Net
|
|
|
178
251
|
#
|
|
179
252
|
# <i>Set membership:</i>
|
|
180
253
|
# - #include? (aliased as #member?):
|
|
181
|
-
# Returns whether a given element
|
|
182
|
-
# contained by the set.
|
|
254
|
+
# Returns whether a given element is contained by the set.
|
|
183
255
|
# - #include_star?: Returns whether the set contains <tt>*</tt>.
|
|
184
256
|
#
|
|
185
257
|
# <i>Minimum and maximum value elements:</i>
|
|
186
|
-
# - #min: Returns the
|
|
187
|
-
# - #max: Returns the
|
|
188
|
-
# - #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.
|
|
189
261
|
#
|
|
190
262
|
# <i>Accessing value by offset in sorted set:</i>
|
|
191
263
|
# - #[] (aliased as #slice): Returns the number or consecutive subset at a
|
|
@@ -252,6 +324,10 @@ module Net
|
|
|
252
324
|
# +self+ and the other set except those common to both.
|
|
253
325
|
# - #~ (aliased as #complement): Returns a new set containing all members
|
|
254
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.
|
|
255
331
|
# - #limit: Returns a copy of +self+ which has replaced <tt>*</tt> with a
|
|
256
332
|
# given maximum value and removed all members over that maximum.
|
|
257
333
|
#
|
|
@@ -318,11 +394,24 @@ module Net
|
|
|
318
394
|
|
|
319
395
|
# valid inputs for "*"
|
|
320
396
|
STARS = [:*, ?*, -1].freeze
|
|
321
|
-
private_constant :
|
|
397
|
+
private_constant :STARS
|
|
322
398
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
private_constant :
|
|
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
|
|
326
415
|
|
|
327
416
|
class << self
|
|
328
417
|
|
|
@@ -337,13 +426,12 @@ module Net
|
|
|
337
426
|
# An empty SequenceSet is invalid and will raise a DataFormatError.
|
|
338
427
|
#
|
|
339
428
|
# Use ::new to create a mutable or empty SequenceSet.
|
|
429
|
+
#
|
|
430
|
+
# Related: ::new, Net::IMAP::SequenceSet(), ::try_convert
|
|
340
431
|
def [](first, *rest)
|
|
341
432
|
if rest.empty?
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
else
|
|
345
|
-
new(first).validate.freeze
|
|
346
|
-
end
|
|
433
|
+
set = try_convert(first)&.validate
|
|
434
|
+
set&.frozen? ? set : (set&.dup || new(first).validate).freeze
|
|
347
435
|
else
|
|
348
436
|
new(first).merge(*rest).validate.freeze
|
|
349
437
|
end
|
|
@@ -356,12 +444,14 @@ module Net
|
|
|
356
444
|
# +to_sequence_set+, calls +obj.to_sequence_set+ and returns the result.
|
|
357
445
|
# Otherwise returns +nil+.
|
|
358
446
|
#
|
|
359
|
-
# If +obj.to_sequence_set+ doesn't return a SequenceSet
|
|
360
|
-
# raised.
|
|
447
|
+
# If +obj.to_sequence_set+ doesn't return a SequenceSet or +nil+, an
|
|
448
|
+
# exception is raised.
|
|
449
|
+
#
|
|
450
|
+
# Related: Net::IMAP::SequenceSet(), ::new, ::[]
|
|
361
451
|
def try_convert(obj)
|
|
362
452
|
return obj if obj.is_a?(SequenceSet)
|
|
363
453
|
return nil unless obj.respond_to?(:to_sequence_set)
|
|
364
|
-
obj = obj.to_sequence_set
|
|
454
|
+
return nil unless obj = obj.to_sequence_set
|
|
365
455
|
return obj if obj.is_a?(SequenceSet)
|
|
366
456
|
raise DataFormatError, "invalid object returned from to_sequence_set"
|
|
367
457
|
end
|
|
@@ -376,23 +466,91 @@ module Net
|
|
|
376
466
|
end
|
|
377
467
|
|
|
378
468
|
# Create a new SequenceSet object from +input+, which may be another
|
|
379
|
-
# SequenceSet, an IMAP formatted +sequence-set+ string, a
|
|
380
|
-
# range, <tt>:*</tt>,
|
|
381
|
-
#
|
|
382
|
-
#
|
|
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.
|
|
383
534
|
def initialize(input = nil) input ? replace(input) : clear end
|
|
384
535
|
|
|
385
536
|
# Removes all elements and returns self.
|
|
386
|
-
def clear
|
|
537
|
+
def clear
|
|
538
|
+
modifying! # redundant check, to normalize the error message for JRuby
|
|
539
|
+
@tuples, @string = [], nil
|
|
540
|
+
self
|
|
541
|
+
end
|
|
387
542
|
|
|
388
543
|
# Replace the contents of the set with the contents of +other+ and returns
|
|
389
544
|
# +self+.
|
|
390
545
|
#
|
|
391
|
-
# +other+ may be another SequenceSet
|
|
392
|
-
#
|
|
546
|
+
# +other+ may be another SequenceSet or any other object that would be
|
|
547
|
+
# accepted by ::new.
|
|
393
548
|
def replace(other)
|
|
394
549
|
case other
|
|
395
|
-
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)
|
|
396
554
|
when String then self.string = other
|
|
397
555
|
else clear; merge other
|
|
398
556
|
end
|
|
@@ -421,32 +579,40 @@ module Net
|
|
|
421
579
|
# If the set was created from a single string, it is not normalized. If
|
|
422
580
|
# the set is updated the string will be normalized.
|
|
423
581
|
#
|
|
424
|
-
# Related: #valid_string, #normalized_string, #to_s
|
|
582
|
+
# Related: #valid_string, #normalized_string, #to_s, #inspect
|
|
425
583
|
def string; @string ||= normalized_string if valid? end
|
|
426
584
|
|
|
427
|
-
#
|
|
428
|
-
#
|
|
429
|
-
|
|
585
|
+
# Returns an array with #normalized_string when valid and an empty array
|
|
586
|
+
# otherwise.
|
|
587
|
+
def deconstruct; valid? ? [normalized_string] : [] end
|
|
588
|
+
|
|
589
|
+
# Assigns a new string to #string and resets #elements to match.
|
|
590
|
+
# Assigning +nil+ or an empty string are equivalent to calling #clear.
|
|
430
591
|
#
|
|
431
|
-
#
|
|
592
|
+
# Non-empty strings are validated but not normalized.
|
|
593
|
+
#
|
|
594
|
+
# Use #add, #merge, or #append to add a string to an existing set.
|
|
432
595
|
#
|
|
433
596
|
# Related: #replace, #clear
|
|
434
|
-
def string=(
|
|
435
|
-
if
|
|
597
|
+
def string=(input)
|
|
598
|
+
if input.nil?
|
|
436
599
|
clear
|
|
437
|
-
|
|
438
|
-
|
|
600
|
+
elsif (str = String.try_convert(input))
|
|
601
|
+
modifying! # short-circuit before parsing the string
|
|
439
602
|
tuples = str_to_tuples str
|
|
440
603
|
@tuples, @string = [], -str
|
|
441
604
|
tuples_add tuples
|
|
605
|
+
else
|
|
606
|
+
raise ArgumentError, "expected a string or nil, got #{input.class}"
|
|
442
607
|
end
|
|
608
|
+
str
|
|
443
609
|
end
|
|
444
610
|
|
|
445
611
|
# Returns the \IMAP +sequence-set+ string representation, or an empty
|
|
446
612
|
# string when the set is empty. Note that an empty set is invalid in the
|
|
447
613
|
# \IMAP syntax.
|
|
448
614
|
#
|
|
449
|
-
# Related: #valid_string, #normalized_string, #
|
|
615
|
+
# Related: #string, #valid_string, #normalized_string, #inspect
|
|
450
616
|
def to_s; string || "" end
|
|
451
617
|
|
|
452
618
|
# Freezes and returns the set. A frozen SequenceSet is Ractor-safe.
|
|
@@ -499,8 +665,9 @@ module Net
|
|
|
499
665
|
|
|
500
666
|
# :call-seq: self === other -> true | false | nil
|
|
501
667
|
#
|
|
502
|
-
# Returns whether +other+ is contained within the set.
|
|
503
|
-
#
|
|
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.
|
|
504
671
|
#
|
|
505
672
|
# Related: #cover?, #include?, #include_star?
|
|
506
673
|
def ===(other)
|
|
@@ -514,12 +681,12 @@ module Net
|
|
|
514
681
|
# Returns whether +other+ is contained within the set. +other+ may be any
|
|
515
682
|
# object that would be accepted by ::new.
|
|
516
683
|
#
|
|
517
|
-
# Related: #===, #include?, #include_star?
|
|
684
|
+
# Related: #===, #include?, #include_star?, #intersect?
|
|
518
685
|
def cover?(other) input_to_tuples(other).none? { !include_tuple?(_1) } end
|
|
519
686
|
|
|
520
687
|
# Returns +true+ when a given number or range is in +self+, and +false+
|
|
521
|
-
# otherwise. Returns +
|
|
522
|
-
# <tt>*</tt
|
|
688
|
+
# otherwise. Returns +nil+ when +number+ isn't a valid SequenceSet
|
|
689
|
+
# element (Integer, Range, <tt>*</tt>, +sequence-set+ string).
|
|
523
690
|
#
|
|
524
691
|
# set = Net::IMAP::SequenceSet["5:10,100,111:115"]
|
|
525
692
|
# set.include? 1 #=> false
|
|
@@ -527,8 +694,8 @@ module Net
|
|
|
527
694
|
# set.include? 11..20 #=> false
|
|
528
695
|
# set.include? 100 #=> true
|
|
529
696
|
# set.include? 6 #=> true, covered by "5:10"
|
|
530
|
-
# set.include?
|
|
531
|
-
# set.include? "
|
|
697
|
+
# set.include? 6..9 #=> true, covered by "5:10"
|
|
698
|
+
# set.include? "6:9" #=> true, strings are parsed
|
|
532
699
|
# set.include? 4..9 #=> false, intersection is not sufficient
|
|
533
700
|
# set.include? "*" #=> false, use #limit to re-interpret "*"
|
|
534
701
|
# set.include? -1 #=> false, -1 is interpreted as "*"
|
|
@@ -537,11 +704,14 @@ module Net
|
|
|
537
704
|
# set.include? :* #=> true
|
|
538
705
|
# set.include? "*" #=> true
|
|
539
706
|
# set.include? -1 #=> true
|
|
540
|
-
# set.include?
|
|
541
|
-
# set.include?
|
|
707
|
+
# set.include?(200..) #=> true
|
|
708
|
+
# set.include?(100..) #=> false
|
|
542
709
|
#
|
|
543
|
-
# Related: #include_star?, #cover?,
|
|
544
|
-
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
|
|
545
715
|
|
|
546
716
|
alias member? include?
|
|
547
717
|
|
|
@@ -554,7 +724,7 @@ module Net
|
|
|
554
724
|
# Net::IMAP::SequenceSet["5:10"].intersect? "7,9,11" #=> true
|
|
555
725
|
# Net::IMAP::SequenceSet["5:10"].intersect? "11:33" #=> false
|
|
556
726
|
#
|
|
557
|
-
# Related: #intersection, #disjoint?
|
|
727
|
+
# Related: #intersection, #disjoint?, #cover?, #include?
|
|
558
728
|
def intersect?(other)
|
|
559
729
|
valid? && input_to_tuples(other).any? { intersect_tuple? _1 }
|
|
560
730
|
end
|
|
@@ -571,26 +741,59 @@ module Net
|
|
|
571
741
|
empty? || input_to_tuples(other).none? { intersect_tuple? _1 }
|
|
572
742
|
end
|
|
573
743
|
|
|
574
|
-
# :call-seq:
|
|
744
|
+
# :call-seq:
|
|
745
|
+
# max(star: :*) => integer or star or nil
|
|
746
|
+
# max(count) => SequenceSet
|
|
575
747
|
#
|
|
576
748
|
# Returns the maximum value in +self+, +star+ when the set includes
|
|
577
749
|
# <tt>*</tt>, or +nil+ when the set is empty.
|
|
578
|
-
|
|
579
|
-
|
|
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
|
+
# n.b: #cardinality has not been backported to 0.5
|
|
759
|
+
cardinality = @tuples.sum(@tuples.count) { _2 - _1 }
|
|
760
|
+
if cardinality <= count
|
|
761
|
+
frozen? ? self : dup
|
|
762
|
+
else
|
|
763
|
+
slice(-count..) || remain_frozen_empty
|
|
764
|
+
end
|
|
765
|
+
elsif (val = @tuples.last&.last)
|
|
766
|
+
val == STAR_INT ? star : val
|
|
767
|
+
end
|
|
580
768
|
end
|
|
581
769
|
|
|
582
|
-
# :call-seq:
|
|
770
|
+
# :call-seq:
|
|
771
|
+
# min(star: :*) => integer or star or nil
|
|
772
|
+
# min(count) => SequenceSet
|
|
583
773
|
#
|
|
584
774
|
# Returns the minimum value in +self+, +star+ when the only value in the
|
|
585
775
|
# set is <tt>*</tt>, or +nil+ when the set is empty.
|
|
586
|
-
|
|
587
|
-
|
|
776
|
+
#
|
|
777
|
+
# When +count+ is given, a new SequenceSet is returned, containing only
|
|
778
|
+
# the first +count+ numbers. An empty SequenceSet is returned when +self+
|
|
779
|
+
# is empty. (+star+ is ignored when +count+ is given.)
|
|
780
|
+
#
|
|
781
|
+
# Related: #max, #minmax, #slice
|
|
782
|
+
def min(count = nil, star: :*)
|
|
783
|
+
if count
|
|
784
|
+
slice(0...count) || remain_frozen_empty
|
|
785
|
+
elsif (val = @tuples.first&.first)
|
|
786
|
+
val != STAR_INT ? val : star
|
|
787
|
+
end
|
|
588
788
|
end
|
|
589
789
|
|
|
590
|
-
# :call-seq: minmax(star: :*) =>
|
|
790
|
+
# :call-seq: minmax(star: :*) => [min, max] or nil
|
|
591
791
|
#
|
|
592
792
|
# Returns a 2-element array containing the minimum and maximum numbers in
|
|
593
|
-
# +self+, or +nil+ when the set is empty.
|
|
793
|
+
# +self+, or +nil+ when the set is empty. +star+ is handled the same way
|
|
794
|
+
# as by #min and #max.
|
|
795
|
+
#
|
|
796
|
+
# Related: #min, #max
|
|
594
797
|
def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end
|
|
595
798
|
|
|
596
799
|
# Returns false when the set is empty.
|
|
@@ -610,14 +813,19 @@ module Net
|
|
|
610
813
|
# Returns a new sequence set that has every number in the +other+ object
|
|
611
814
|
# added.
|
|
612
815
|
#
|
|
613
|
-
# +other+ may be any object that would be accepted by ::new
|
|
614
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
615
|
-
# another sequence set, or an enumerable containing any of these.
|
|
816
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
616
817
|
#
|
|
617
818
|
# Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
|
|
618
819
|
# #=> Net::IMAP::SequenceSet["1:6,99"]
|
|
619
820
|
#
|
|
620
|
-
# Related: #add, #merge
|
|
821
|
+
# Related: #add, #merge, #&, #-, #^, #~
|
|
822
|
+
#
|
|
823
|
+
# ==== Set identities
|
|
824
|
+
#
|
|
825
|
+
# <tt>lhs | rhs</tt> is equivalent to:
|
|
826
|
+
# * <tt>rhs | lhs</tt> (commutative)
|
|
827
|
+
# * <tt>~(~lhs & ~rhs)</tt> (De Morgan's Law)
|
|
828
|
+
# * <tt>(lhs & rhs) ^ (lhs ^ rhs)</tt>
|
|
621
829
|
def |(other) remain_frozen dup.merge other end
|
|
622
830
|
alias :+ :|
|
|
623
831
|
alias union :|
|
|
@@ -629,14 +837,22 @@ module Net
|
|
|
629
837
|
# Returns a new sequence set built by duplicating this set and removing
|
|
630
838
|
# every number that appears in +other+.
|
|
631
839
|
#
|
|
632
|
-
# +other+ may be any object that would be accepted by ::new
|
|
633
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
634
|
-
# another sequence set, or an enumerable containing any of these.
|
|
840
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
635
841
|
#
|
|
636
842
|
# Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
|
|
637
843
|
# #=> Net::IMAP::SequenceSet["1,3,5"]
|
|
638
844
|
#
|
|
639
|
-
# Related: #subtract
|
|
845
|
+
# Related: #subtract, #|, #&, #^, #~
|
|
846
|
+
#
|
|
847
|
+
# ==== Set identities
|
|
848
|
+
#
|
|
849
|
+
# <tt>lhs - rhs</tt> is equivalent to:
|
|
850
|
+
# * <tt>~rhs - ~lhs</tt>
|
|
851
|
+
# * <tt>lhs & ~rhs</tt>
|
|
852
|
+
# * <tt>~(~lhs | rhs)</tt>
|
|
853
|
+
# * <tt>lhs & (lhs ^ rhs)</tt>
|
|
854
|
+
# * <tt>lhs ^ (lhs & rhs)</tt>
|
|
855
|
+
# * <tt>rhs ^ (lhs | rhs)</tt>
|
|
640
856
|
def -(other) remain_frozen dup.subtract other end
|
|
641
857
|
alias difference :-
|
|
642
858
|
|
|
@@ -647,14 +863,22 @@ module Net
|
|
|
647
863
|
# Returns a new sequence set containing only the numbers common to this
|
|
648
864
|
# set and +other+.
|
|
649
865
|
#
|
|
650
|
-
# +other+ may be any object that would be accepted by ::new
|
|
651
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
652
|
-
# another sequence set, or an enumerable containing any of these.
|
|
866
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
653
867
|
#
|
|
654
868
|
# Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
|
|
655
869
|
# #=> Net::IMAP::SequenceSet["2,4"]
|
|
656
870
|
#
|
|
657
|
-
#
|
|
871
|
+
# Related: #intersect?, #|, #-, #^, #~
|
|
872
|
+
#
|
|
873
|
+
# ==== Set identities
|
|
874
|
+
#
|
|
875
|
+
# <tt>lhs & rhs</tt> is equivalent to:
|
|
876
|
+
# * <tt>rhs & lhs</tt> (commutative)
|
|
877
|
+
# * <tt>~(~lhs | ~rhs)</tt> (De Morgan's Law)
|
|
878
|
+
# * <tt>lhs - ~rhs</tt>
|
|
879
|
+
# * <tt>lhs - (lhs - rhs)</tt>
|
|
880
|
+
# * <tt>lhs - (lhs ^ rhs)</tt>
|
|
881
|
+
# * <tt>lhs ^ (lhs - rhs)</tt>
|
|
658
882
|
def &(other)
|
|
659
883
|
remain_frozen dup.subtract SequenceSet.new(other).complement!
|
|
660
884
|
end
|
|
@@ -667,15 +891,21 @@ module Net
|
|
|
667
891
|
# Returns a new sequence set containing numbers that are exclusive between
|
|
668
892
|
# this set and +other+.
|
|
669
893
|
#
|
|
670
|
-
# +other+ may be any object that would be accepted by ::new
|
|
671
|
-
# bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
|
|
672
|
-
# another sequence set, or an enumerable containing any of these.
|
|
894
|
+
# +other+ may be any object that would be accepted by ::new.
|
|
673
895
|
#
|
|
674
896
|
# Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
|
|
675
897
|
# #=> Net::IMAP::SequenceSet["1,3,5:6"]
|
|
676
898
|
#
|
|
677
|
-
#
|
|
678
|
-
#
|
|
899
|
+
# Related: #|, #&, #-, #~
|
|
900
|
+
#
|
|
901
|
+
# ==== Set identities
|
|
902
|
+
#
|
|
903
|
+
# <tt>lhs ^ rhs</tt> is equivalent to:
|
|
904
|
+
# * <tt>rhs ^ lhs</tt> (commutative)
|
|
905
|
+
# * <tt>~lhs ^ ~rhs</tt>
|
|
906
|
+
# * <tt>(lhs | rhs) - (lhs & rhs)</tt>
|
|
907
|
+
# * <tt>(lhs - rhs) | (rhs - lhs)</tt>
|
|
908
|
+
# * <tt>(lhs ^ other) ^ (other ^ rhs)</tt>
|
|
679
909
|
def ^(other) remain_frozen (dup | other).subtract(self & other) end
|
|
680
910
|
alias xor :^
|
|
681
911
|
|
|
@@ -693,7 +923,12 @@ module Net
|
|
|
693
923
|
# ~Net::IMAP::SequenceSet["6:99,223:*"]
|
|
694
924
|
# #=> Net::IMAP::SequenceSet["1:5,100:222"]
|
|
695
925
|
#
|
|
696
|
-
# Related: #complement
|
|
926
|
+
# Related: #complement!, #|, #&, #-, #^
|
|
927
|
+
#
|
|
928
|
+
# ==== Set identities
|
|
929
|
+
#
|
|
930
|
+
# <tt>~set</tt> is equivalent to:
|
|
931
|
+
# * <tt>full - set</tt>, where "full" is Net::IMAP::SequenceSet.full
|
|
697
932
|
def ~; remain_frozen dup.complement! end
|
|
698
933
|
alias complement :~
|
|
699
934
|
|
|
@@ -705,8 +940,12 @@ module Net
|
|
|
705
940
|
#
|
|
706
941
|
# #string will be regenerated. Use #merge to add many elements at once.
|
|
707
942
|
#
|
|
708
|
-
#
|
|
943
|
+
# Use #append to append new elements to #string. See
|
|
944
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
945
|
+
#
|
|
946
|
+
# Related: #add?, #merge, #union, #append
|
|
709
947
|
def add(element)
|
|
948
|
+
modifying! # short-circuit before input_to_tuple
|
|
710
949
|
tuple_add input_to_tuple element
|
|
711
950
|
normalize!
|
|
712
951
|
end
|
|
@@ -716,8 +955,12 @@ module Net
|
|
|
716
955
|
#
|
|
717
956
|
# Unlike #add, #merge, or #union, the new value is appended to #string.
|
|
718
957
|
# This may result in a #string which has duplicates or is out-of-order.
|
|
958
|
+
#
|
|
959
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
960
|
+
#
|
|
961
|
+
# Related: #add, #merge, #union
|
|
719
962
|
def append(entry)
|
|
720
|
-
modifying!
|
|
963
|
+
modifying! # short-circuit before input_to_tuple
|
|
721
964
|
tuple = input_to_tuple entry
|
|
722
965
|
entry = tuple_to_str tuple
|
|
723
966
|
string unless empty? # write @string before tuple_add
|
|
@@ -735,6 +978,7 @@ module Net
|
|
|
735
978
|
#
|
|
736
979
|
# Related: #add, #merge, #union, #include?
|
|
737
980
|
def add?(element)
|
|
981
|
+
modifying! # short-circuit before include?
|
|
738
982
|
add element unless include? element
|
|
739
983
|
end
|
|
740
984
|
|
|
@@ -747,6 +991,7 @@ module Net
|
|
|
747
991
|
#
|
|
748
992
|
# Related: #delete?, #delete_at, #subtract, #difference
|
|
749
993
|
def delete(element)
|
|
994
|
+
modifying! # short-circuit before input_to_tuple
|
|
750
995
|
tuple_subtract input_to_tuple element
|
|
751
996
|
normalize!
|
|
752
997
|
end
|
|
@@ -784,8 +1029,10 @@ module Net
|
|
|
784
1029
|
#
|
|
785
1030
|
# Related: #delete, #delete_at, #subtract, #difference, #disjoint?
|
|
786
1031
|
def delete?(element)
|
|
1032
|
+
modifying! # short-circuit before input_to_tuple
|
|
1033
|
+
element = input_try_convert(element)
|
|
787
1034
|
tuple = input_to_tuple element
|
|
788
|
-
if
|
|
1035
|
+
if number_input?(element)
|
|
789
1036
|
return unless include_tuple? tuple
|
|
790
1037
|
tuple_subtract tuple
|
|
791
1038
|
normalize!
|
|
@@ -824,6 +1071,7 @@ module Net
|
|
|
824
1071
|
#
|
|
825
1072
|
# Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference
|
|
826
1073
|
def slice!(index, length = nil)
|
|
1074
|
+
modifying! # short-circuit before slice
|
|
827
1075
|
deleted = slice(index, length) and subtract deleted
|
|
828
1076
|
deleted
|
|
829
1077
|
end
|
|
@@ -831,14 +1079,13 @@ module Net
|
|
|
831
1079
|
# Merges all of the elements that appear in any of the +sets+ into the
|
|
832
1080
|
# set, and returns +self+.
|
|
833
1081
|
#
|
|
834
|
-
# The +sets+ may be any objects that would be accepted by ::new
|
|
835
|
-
# 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
|
|
836
|
-
# strings, other sequence sets, or enumerables containing any of these.
|
|
1082
|
+
# The +sets+ may be any objects that would be accepted by ::new.
|
|
837
1083
|
#
|
|
838
1084
|
# #string will be regenerated after all sets have been merged.
|
|
839
1085
|
#
|
|
840
1086
|
# Related: #add, #add?, #union
|
|
841
1087
|
def merge(*sets)
|
|
1088
|
+
modifying! # short-circuit before input_to_tuples
|
|
842
1089
|
tuples_add input_to_tuples sets
|
|
843
1090
|
normalize!
|
|
844
1091
|
end
|
|
@@ -846,9 +1093,7 @@ module Net
|
|
|
846
1093
|
# Removes all of the elements that appear in any of the given +sets+ from
|
|
847
1094
|
# the set, and returns +self+.
|
|
848
1095
|
#
|
|
849
|
-
# The +sets+ may be any objects that would be accepted by ::new
|
|
850
|
-
# 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
|
|
851
|
-
# strings, other sequence sets, or enumerables containing any of these.
|
|
1096
|
+
# The +sets+ may be any objects that would be accepted by ::new.
|
|
852
1097
|
#
|
|
853
1098
|
# Related: #difference
|
|
854
1099
|
def subtract(*sets)
|
|
@@ -864,21 +1109,21 @@ module Net
|
|
|
864
1109
|
# This is useful when the given order is significant, for example in a
|
|
865
1110
|
# ESEARCH response to IMAP#sort.
|
|
866
1111
|
#
|
|
1112
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1113
|
+
#
|
|
867
1114
|
# Related: #each_entry, #elements
|
|
868
1115
|
def entries; each_entry.to_a end
|
|
869
1116
|
|
|
870
1117
|
# Returns an array of ranges and integers and <tt>:*</tt>.
|
|
871
1118
|
#
|
|
872
1119
|
# The returned elements are sorted and coalesced, even when the input
|
|
873
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
|
1120
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
|
1121
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
874
1122
|
#
|
|
875
1123
|
# By itself, <tt>*</tt> translates to <tt>:*</tt>. A range containing
|
|
876
1124
|
# <tt>*</tt> translates to an endless range. Use #limit to translate both
|
|
877
1125
|
# cases to a maximum value.
|
|
878
1126
|
#
|
|
879
|
-
# The returned elements will be sorted and coalesced, even when the input
|
|
880
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize.
|
|
881
|
-
#
|
|
882
1127
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
|
|
883
1128
|
# #=> [2, 5..9, 11..12, :*]
|
|
884
1129
|
#
|
|
@@ -889,15 +1134,13 @@ module Net
|
|
|
889
1134
|
# Returns an array of ranges
|
|
890
1135
|
#
|
|
891
1136
|
# The returned elements are sorted and coalesced, even when the input
|
|
892
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
|
1137
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
|
1138
|
+
# SequenceSet@Ordered+and+Normalized+sets.
|
|
893
1139
|
#
|
|
894
1140
|
# <tt>*</tt> translates to an endless range. By itself, <tt>*</tt>
|
|
895
1141
|
# translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
|
|
896
1142
|
# value.
|
|
897
1143
|
#
|
|
898
|
-
# The returned ranges will be sorted and coalesced, even when the input
|
|
899
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize.
|
|
900
|
-
#
|
|
901
1144
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
|
|
902
1145
|
# #=> [2..2, 5..9, 11..12, :*..]
|
|
903
1146
|
# Net::IMAP::SequenceSet["123,999:*,456:789"].ranges
|
|
@@ -909,7 +1152,7 @@ module Net
|
|
|
909
1152
|
# Returns a sorted array of all of the number values in the sequence set.
|
|
910
1153
|
#
|
|
911
1154
|
# The returned numbers are sorted and de-duplicated, even when the input
|
|
912
|
-
# #string is not. See #normalize.
|
|
1155
|
+
# #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
|
|
913
1156
|
#
|
|
914
1157
|
# Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
|
|
915
1158
|
# #=> [2, 5, 6, 7, 8, 9, 11, 12]
|
|
@@ -941,6 +1184,8 @@ module Net
|
|
|
941
1184
|
# no sorting, deduplication, or coalescing. When #string is in its
|
|
942
1185
|
# normalized form, this will yield the same values as #each_element.
|
|
943
1186
|
#
|
|
1187
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1188
|
+
#
|
|
944
1189
|
# Related: #entries, #each_element
|
|
945
1190
|
def each_entry(&block) # :yields: integer or range or :*
|
|
946
1191
|
return to_enum(__method__) unless block_given?
|
|
@@ -951,7 +1196,7 @@ module Net
|
|
|
951
1196
|
# and returns self. Returns an enumerator when called without a block.
|
|
952
1197
|
#
|
|
953
1198
|
# The returned numbers are sorted and de-duplicated, even when the input
|
|
954
|
-
# #string is not. See #normalize.
|
|
1199
|
+
# #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
|
|
955
1200
|
#
|
|
956
1201
|
# Related: #elements, #each_entry
|
|
957
1202
|
def each_element # :yields: integer or range or :*
|
|
@@ -1263,6 +1508,58 @@ module Net
|
|
|
1263
1508
|
|
|
1264
1509
|
public
|
|
1265
1510
|
|
|
1511
|
+
# Returns a copy of +self+ which only contains the numbers above +num+.
|
|
1512
|
+
#
|
|
1513
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
|
|
1514
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(20) # to_s => "21:22,50
|
|
1515
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(30) # to_s => "50"
|
|
1516
|
+
#
|
|
1517
|
+
# This returns the same result as #intersection with <tt>((num+1)..)</tt>
|
|
1518
|
+
# or #difference with <tt>(..num)</tt>.
|
|
1519
|
+
#
|
|
1520
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (11..) # to_s => "11:22,50"
|
|
1521
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..10) # to_s => "11:22,50"
|
|
1522
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (21..) # to_s => "21:22,50"
|
|
1523
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..20) # to_s => "21:22,50"
|
|
1524
|
+
#
|
|
1525
|
+
# Related: #above, #-, #&
|
|
1526
|
+
def above(num)
|
|
1527
|
+
NumValidator.valid_nz_number?(num) or
|
|
1528
|
+
raise ArgumentError, "not a valid sequence set number"
|
|
1529
|
+
difference(..num)
|
|
1530
|
+
end
|
|
1531
|
+
|
|
1532
|
+
# Returns a copy of +self+ which only contains numbers below +num+.
|
|
1533
|
+
#
|
|
1534
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(10) # to_s => "5"
|
|
1535
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(20) # to_s => "5,10:19"
|
|
1536
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
|
1537
|
+
#
|
|
1538
|
+
# This returns the same result as #intersection with <tt>(..(num-1))</tt>
|
|
1539
|
+
# or #difference with <tt>(num..)</tt>.
|
|
1540
|
+
#
|
|
1541
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..9) # to_s => "5"
|
|
1542
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (10..) # to_s => "5"
|
|
1543
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..19) # to_s => "5,10:19"
|
|
1544
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (20..) # to_s => "5,10:19"
|
|
1545
|
+
#
|
|
1546
|
+
# When the set does not contain <tt>*</tt>, #below is identical to #limit
|
|
1547
|
+
# with <tt>max: num - 1</tt>. When the set does contain <tt>*</tt>,
|
|
1548
|
+
# #below always drops it from the result. Use #limit when the IMAP
|
|
1549
|
+
# semantics for <tt>*</tt> must be enforced.
|
|
1550
|
+
#
|
|
1551
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
|
1552
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 29) # to_s => "5,10:22"
|
|
1553
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].below(30) # to_s => "5,10:22"
|
|
1554
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].limit(max: 29) # to_s => "5,10:22,29"
|
|
1555
|
+
#
|
|
1556
|
+
# Related: #above, #-, #&, #limit
|
|
1557
|
+
def below(num)
|
|
1558
|
+
NumValidator.valid_nz_number?(num) or
|
|
1559
|
+
raise ArgumentError, "not a valid sequence set number"
|
|
1560
|
+
difference(num..)
|
|
1561
|
+
end
|
|
1562
|
+
|
|
1266
1563
|
# Returns a frozen SequenceSet with <tt>*</tt> converted to +max+, numbers
|
|
1267
1564
|
# and ranges over +max+ removed, and ranges containing +max+ converted to
|
|
1268
1565
|
# end at +max+.
|
|
@@ -1280,6 +1577,7 @@ module Net
|
|
|
1280
1577
|
# Net::IMAP::SequenceSet["500:*"].limit(max: 37)
|
|
1281
1578
|
# #=> Net::IMAP::SequenceSet["37"]
|
|
1282
1579
|
#
|
|
1580
|
+
# Related: #limit!
|
|
1283
1581
|
def limit(max:)
|
|
1284
1582
|
max = to_tuple_int(max)
|
|
1285
1583
|
if empty? then self.class.empty
|
|
@@ -1294,6 +1592,7 @@ module Net
|
|
|
1294
1592
|
#
|
|
1295
1593
|
# Related: #limit
|
|
1296
1594
|
def limit!(max:)
|
|
1595
|
+
modifying! # short-circuit, and normalize the error message for JRuby
|
|
1297
1596
|
star = include_star?
|
|
1298
1597
|
max = to_tuple_int(max)
|
|
1299
1598
|
tuple_subtract [max + 1, STAR_INT]
|
|
@@ -1308,6 +1607,7 @@ module Net
|
|
|
1308
1607
|
#
|
|
1309
1608
|
# Related: #complement
|
|
1310
1609
|
def complement!
|
|
1610
|
+
modifying! # short-circuit, and normalize the error message for JRuby
|
|
1311
1611
|
return replace(self.class.full) if empty?
|
|
1312
1612
|
return clear if full?
|
|
1313
1613
|
flat = @tuples.flat_map { [_1 - 1, _2 + 1] }
|
|
@@ -1321,6 +1621,7 @@ module Net
|
|
|
1321
1621
|
#
|
|
1322
1622
|
# The returned set's #string is sorted and deduplicated. Adjacent or
|
|
1323
1623
|
# overlapping elements will be merged into a single larger range.
|
|
1624
|
+
# See SequenceSet@Ordered+and+Normalized+sets.
|
|
1324
1625
|
#
|
|
1325
1626
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
|
|
1326
1627
|
# #=> Net::IMAP::SequenceSet["1:7,9:11"]
|
|
@@ -1333,37 +1634,87 @@ module Net
|
|
|
1333
1634
|
end
|
|
1334
1635
|
|
|
1335
1636
|
# Resets #string to be sorted, deduplicated, and coalesced. Returns
|
|
1336
|
-
# +self+.
|
|
1637
|
+
# +self+. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1337
1638
|
#
|
|
1338
1639
|
# Related: #normalize, #normalized_string
|
|
1339
1640
|
def normalize!
|
|
1641
|
+
modifying! # redundant check, to normalize the error message for JRuby
|
|
1340
1642
|
@string = nil
|
|
1341
1643
|
self
|
|
1342
1644
|
end
|
|
1343
1645
|
|
|
1344
1646
|
# Returns a normalized +sequence-set+ string representation, sorted
|
|
1345
1647
|
# and deduplicated. Adjacent or overlapping elements will be merged into
|
|
1346
|
-
# a single larger range.
|
|
1648
|
+
# a single larger range. See SequenceSet@Ordered+and+Normalized+sets.
|
|
1347
1649
|
#
|
|
1348
1650
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
|
|
1349
1651
|
# #=> "1:7,9:11"
|
|
1350
1652
|
#
|
|
1351
|
-
#
|
|
1653
|
+
# Returns +nil+ when the set is empty.
|
|
1654
|
+
#
|
|
1655
|
+
# Related: #normalize!, #normalize, #string, #to_s
|
|
1352
1656
|
def normalized_string
|
|
1353
1657
|
@tuples.empty? ? nil : -@tuples.map { tuple_to_str _1 }.join(",")
|
|
1354
1658
|
end
|
|
1355
1659
|
|
|
1660
|
+
# Returns an inspection string for the SequenceSet.
|
|
1661
|
+
#
|
|
1662
|
+
# Net::IMAP::SequenceSet.new.inspect
|
|
1663
|
+
# #=> "Net::IMAP::SequenceSet()"
|
|
1664
|
+
#
|
|
1665
|
+
# Net::IMAP::SequenceSet(1..5, 1024, 15, 2000).inspect
|
|
1666
|
+
# #=> 'Net::IMAP::SequenceSet("1:5,15,1024,2000")'
|
|
1667
|
+
#
|
|
1668
|
+
# Frozen sets have slightly different output:
|
|
1669
|
+
#
|
|
1670
|
+
# Net::IMAP::SequenceSet.empty.inspect
|
|
1671
|
+
# #=> "Net::IMAP::SequenceSet.empty"
|
|
1672
|
+
#
|
|
1673
|
+
# Net::IMAP::SequenceSet[1..5, 1024, 15, 2000].inspect
|
|
1674
|
+
# #=> 'Net::IMAP::SequenceSet["1:5,15,1024,2000"]'
|
|
1675
|
+
#
|
|
1676
|
+
# Large sets (by number of #entries) have abridged output, with only the
|
|
1677
|
+
# first and last entries:
|
|
1678
|
+
#
|
|
1679
|
+
# Net::IMAP::SequenceSet(((1..5000) % 2).to_a).inspect
|
|
1680
|
+
# #=> #<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">
|
|
1681
|
+
#
|
|
1682
|
+
# Related: #to_s, #string
|
|
1356
1683
|
def inspect
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1684
|
+
case (count = count_entries)
|
|
1685
|
+
when 0
|
|
1686
|
+
(frozen? ? "%s.empty" : "%s()") % [self.class]
|
|
1687
|
+
when ..INSPECT_MAX_LEN
|
|
1688
|
+
(frozen? ? "%s[%p]" : "%s(%p)") % [self.class, to_s]
|
|
1361
1689
|
else
|
|
1362
|
-
|
|
1690
|
+
if @string
|
|
1691
|
+
head = @string[INSPECT_ABRIDGED_HEAD_RE]
|
|
1692
|
+
tail = @string[INSPECT_ABRIDGED_TAIL_RE]
|
|
1693
|
+
else
|
|
1694
|
+
head = export_string_entries(@tuples.first(INSPECT_TRUNCATE_LEN)) + ","
|
|
1695
|
+
tail = "," + export_string_entries(@tuples.last(INSPECT_TRUNCATE_LEN))
|
|
1696
|
+
end
|
|
1697
|
+
'#<%s %d entries "%s...(%d entries omitted)...%s"%s>' % [
|
|
1698
|
+
self.class, count,
|
|
1699
|
+
head, count - INSPECT_TRUNCATE_LEN * 2, tail,
|
|
1700
|
+
frozen? ? " (frozen)" : "",
|
|
1701
|
+
]
|
|
1363
1702
|
end
|
|
1364
1703
|
end
|
|
1365
1704
|
|
|
1366
|
-
|
|
1705
|
+
private def count_entries
|
|
1706
|
+
@string ? @string.count(",") + 1 : @tuples.count
|
|
1707
|
+
end
|
|
1708
|
+
|
|
1709
|
+
##
|
|
1710
|
+
# :method: to_sequence_set
|
|
1711
|
+
# :call-seq: to_sequence_set -> self
|
|
1712
|
+
#
|
|
1713
|
+
# Returns +self+
|
|
1714
|
+
#
|
|
1715
|
+
# Related: ::try_convert
|
|
1716
|
+
|
|
1717
|
+
# :nodoc: (work around rdoc bug)
|
|
1367
1718
|
alias to_sequence_set itself
|
|
1368
1719
|
|
|
1369
1720
|
# Unstable API: currently for internal use only (Net::IMAP#validate_data)
|
|
@@ -1377,10 +1728,24 @@ module Net
|
|
|
1377
1728
|
imap.__send__(:put_string, valid_string)
|
|
1378
1729
|
end
|
|
1379
1730
|
|
|
1731
|
+
# For YAML serialization
|
|
1732
|
+
def encode_with(coder) # :nodoc:
|
|
1733
|
+
# we can perfectly reconstruct from the string
|
|
1734
|
+
coder['string'] = to_s
|
|
1735
|
+
end
|
|
1736
|
+
|
|
1737
|
+
# For YAML deserialization
|
|
1738
|
+
def init_with(coder) # :nodoc:
|
|
1739
|
+
@tuples = []
|
|
1740
|
+
self.string = coder['string']
|
|
1741
|
+
end
|
|
1742
|
+
|
|
1380
1743
|
protected
|
|
1381
1744
|
|
|
1382
1745
|
attr_reader :tuples # :nodoc:
|
|
1383
1746
|
|
|
1747
|
+
def deep_copy_tuples; @tuples.map { _1.dup } end # :nodoc:
|
|
1748
|
+
|
|
1384
1749
|
private
|
|
1385
1750
|
|
|
1386
1751
|
def remain_frozen(set) frozen? ? set.freeze : set end
|
|
@@ -1388,12 +1753,12 @@ module Net
|
|
|
1388
1753
|
|
|
1389
1754
|
# frozen clones are shallow copied
|
|
1390
1755
|
def initialize_clone(other)
|
|
1391
|
-
other.
|
|
1756
|
+
@tuples = other.deep_copy_tuples unless other.frozen?
|
|
1757
|
+
super
|
|
1392
1758
|
end
|
|
1393
1759
|
|
|
1394
1760
|
def initialize_dup(other)
|
|
1395
|
-
@tuples = other.
|
|
1396
|
-
@string = other.string&.-@
|
|
1761
|
+
@tuples = other.deep_copy_tuples
|
|
1397
1762
|
super
|
|
1398
1763
|
end
|
|
1399
1764
|
|
|
@@ -1414,12 +1779,12 @@ module Net
|
|
|
1414
1779
|
when *STARS, Integer, Range then [input_to_tuple(set)]
|
|
1415
1780
|
when String then str_to_tuples set
|
|
1416
1781
|
when SequenceSet then set.tuples
|
|
1417
|
-
when
|
|
1782
|
+
when Set then set.map { [to_tuple_int(_1)] * 2 }
|
|
1783
|
+
when Array then set.flat_map { input_to_tuples _1 }
|
|
1418
1784
|
when nil then []
|
|
1419
1785
|
else
|
|
1420
|
-
raise DataFormatError,
|
|
1421
|
-
|
|
1422
|
-
"got %p" % [set]
|
|
1786
|
+
raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
|
|
1787
|
+
"got %p" % [set]
|
|
1423
1788
|
end
|
|
1424
1789
|
end
|
|
1425
1790
|
|
|
@@ -1427,12 +1792,19 @@ module Net
|
|
|
1427
1792
|
# String, Set, Array, or... any type of object.
|
|
1428
1793
|
def input_try_convert(input)
|
|
1429
1794
|
SequenceSet.try_convert(input) ||
|
|
1430
|
-
|
|
1431
|
-
input.respond_to?(:to_int) && Integer(input.to_int) ||
|
|
1795
|
+
Integer.try_convert(input) ||
|
|
1432
1796
|
String.try_convert(input) ||
|
|
1433
1797
|
input
|
|
1434
1798
|
end
|
|
1435
1799
|
|
|
1800
|
+
# NOTE: input_try_convert must be called on input first
|
|
1801
|
+
def number_input?(input)
|
|
1802
|
+
case input
|
|
1803
|
+
when *STARS, Integer then true
|
|
1804
|
+
when String then !input.include?(/[:,]/)
|
|
1805
|
+
end
|
|
1806
|
+
end
|
|
1807
|
+
|
|
1436
1808
|
def range_to_tuple(range)
|
|
1437
1809
|
first = to_tuple_int(range.begin || 1)
|
|
1438
1810
|
last = to_tuple_int(range.end || :*)
|
|
@@ -1446,6 +1818,10 @@ module Net
|
|
|
1446
1818
|
def to_tuple_int(obj) STARS.include?(obj) ? STAR_INT : nz_number(obj) end
|
|
1447
1819
|
def from_tuple_int(num) num == STAR_INT ? :* : num end
|
|
1448
1820
|
|
|
1821
|
+
def export_string_entries(entries)
|
|
1822
|
+
-entries.map { tuple_to_str _1 }.join(",")
|
|
1823
|
+
end
|
|
1824
|
+
|
|
1449
1825
|
def tuple_to_str(tuple) tuple.uniq.map{ from_tuple_int _1 }.join(":") end
|
|
1450
1826
|
def str_to_tuples(str) str.split(",", -1).map! { str_to_tuple _1 } end
|
|
1451
1827
|
def str_to_tuple(str)
|
|
@@ -1558,12 +1934,11 @@ module Net
|
|
|
1558
1934
|
end
|
|
1559
1935
|
|
|
1560
1936
|
def nz_number(num)
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
num
|
|
1937
|
+
String === num && !/\A[1-9]\d*\z/.match?(num) and
|
|
1938
|
+
raise DataFormatError, "%p is not a valid nz-number" % [num]
|
|
1939
|
+
NumValidator.ensure_nz_number Integer num
|
|
1940
|
+
rescue TypeError # To catch errors from Integer()
|
|
1941
|
+
raise DataFormatError, $!.message
|
|
1567
1942
|
end
|
|
1568
1943
|
|
|
1569
1944
|
# intentionally defined after the class implementation
|