net-imap 0.4.10 → 0.5.8
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/BSDL +22 -0
- data/COPYING +56 -0
- data/Gemfile +8 -1
- data/LICENSE.txt +3 -22
- 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 +61 -48
- data/lib/net/imap/config/attr_accessors.rb +75 -0
- data/lib/net/imap/config/attr_inheritance.rb +90 -0
- data/lib/net/imap/config/attr_type_coercion.rb +62 -0
- data/lib/net/imap/config.rb +552 -0
- data/lib/net/imap/connection_state.rb +48 -0
- data/lib/net/imap/data_encoding.rb +4 -4
- data/lib/net/imap/data_lite.rb +226 -0
- data/lib/net/imap/deprecated_client_options.rb +9 -6
- data/lib/net/imap/errors.rb +40 -1
- data/lib/net/imap/esearch_result.rb +180 -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 +126 -239
- data/lib/net/imap/response_parser/parser_utils.rb +11 -6
- data/lib/net/imap/response_parser.rb +188 -34
- data/lib/net/imap/response_reader.rb +73 -0
- 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 +3 -3
- 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 +8 -8
- data/lib/net/imap/sasl.rb +8 -5
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/search_result.rb +2 -2
- data/lib/net/imap/sequence_set.rb +471 -176
- 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 +244 -0
- data/lib/net/imap/vanished_data.rb +56 -0
- data/lib/net/imap.rb +1225 -364
- data/net-imap.gemspec +4 -4
- data/rakelib/benchmarks.rake +1 -1
- data/rakelib/rfcs.rake +2 -0
- data/rakelib/string_prep_tables_generator.rb +2 -0
- metadata +17 -12
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/pages.yml +0 -46
- data/.github/workflows/test.yml +0 -31
- data/.gitignore +0 -12
@@ -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,13 +16,6 @@ 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
21
|
# SequenceSet.new with no arguments creates an empty sequence set. Note
|
@@ -37,7 +32,8 @@ module Net
|
|
37
32
|
#
|
38
33
|
# SequenceSet.new may receive a single optional argument: a non-zero 32 bit
|
39
34
|
# unsigned integer, a range, a <tt>sequence-set</tt> formatted string,
|
40
|
-
# another sequence set,
|
35
|
+
# another sequence set, a Set (containing only numbers or <tt>*</tt>), or an
|
36
|
+
# Array containing any of these (array inputs may be nested).
|
41
37
|
#
|
42
38
|
# set = Net::IMAP::SequenceSet.new(1)
|
43
39
|
# set.valid_string #=> "1"
|
@@ -60,18 +56,20 @@ module Net
|
|
60
56
|
# set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
|
61
57
|
# set.valid_string #=> "1:10,55,1024:2048"
|
62
58
|
#
|
63
|
-
# == Normalized
|
59
|
+
# == Ordered and Normalized sets
|
64
60
|
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
# methods use this normalized representation. Most modification methods
|
70
|
-
# will convert #string to its normalized form.
|
61
|
+
# Sometimes the order of the set's members is significant, such as with the
|
62
|
+
# +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
|
63
|
+
# sequence set is created by the parser or with a single string value, that
|
64
|
+
# #string representation is preserved.
|
71
65
|
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
66
|
+
# Internally, SequenceSet stores a normalized representation which sorts all
|
67
|
+
# entries, de-duplicates numbers, and coalesces adjacent or overlapping
|
68
|
+
# ranges. Most methods use this normalized representation to achieve
|
69
|
+
# <tt>O(lg n)</tt> porformance. Use #entries or #each_entry to enumerate
|
70
|
+
# the set in its original order.
|
71
|
+
#
|
72
|
+
# Most modification methods convert #string to its normalized form. To
|
75
73
|
# preserve #string order while modifying a set, use #append, #string=, or
|
76
74
|
# #replace.
|
77
75
|
#
|
@@ -110,11 +108,15 @@ module Net
|
|
110
108
|
# When a set includes <tt>*</tt>, some methods may have surprising behavior.
|
111
109
|
#
|
112
110
|
# For example, #complement treats <tt>*</tt> as its own number. This way,
|
113
|
-
# the #intersection of a set and its #complement will always be empty.
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
111
|
+
# the #intersection of a set and its #complement will always be empty. And
|
112
|
+
# <tt>*</tt> is sorted as greater than any other number in the set. This is
|
113
|
+
# not how an \IMAP server interprets the set: it will convert <tt>*</tt> to
|
114
|
+
# the number of messages in the mailbox, the +UID+ of the last message in
|
115
|
+
# the mailbox, or +UIDNEXT+, as appropriate. Several methods have an
|
116
|
+
# argument for how <tt>*</tt> should be interpreted.
|
117
|
+
#
|
118
|
+
# But, for example, this means that there may be overlap between a set and
|
119
|
+
# its complement after #limit is applied to each:
|
118
120
|
#
|
119
121
|
# ~Net::IMAP::SequenceSet["*"] == Net::IMAP::SequenceSet[1..(2**32-1)]
|
120
122
|
# ~Net::IMAP::SequenceSet[1..5] == Net::IMAP::SequenceSet["6:*"]
|
@@ -164,7 +166,7 @@ module Net
|
|
164
166
|
# - #===:
|
165
167
|
# Returns whether a given object is fully contained within +self+, or
|
166
168
|
# +nil+ if the object cannot be converted to a compatible type.
|
167
|
-
# - #cover
|
169
|
+
# - #cover?:
|
168
170
|
# Returns whether a given object is fully contained within +self+.
|
169
171
|
# - #intersect? (aliased as #overlap?):
|
170
172
|
# Returns whether +self+ and a given object have any common elements.
|
@@ -176,39 +178,50 @@ module Net
|
|
176
178
|
#
|
177
179
|
# <i>Set membership:</i>
|
178
180
|
# - #include? (aliased as #member?):
|
179
|
-
# Returns whether a given
|
181
|
+
# Returns whether a given element (nz-number, range, or <tt>*</tt>) is
|
180
182
|
# contained by the set.
|
181
183
|
# - #include_star?: Returns whether the set contains <tt>*</tt>.
|
182
184
|
#
|
183
185
|
# <i>Minimum and maximum value elements:</i>
|
184
|
-
# - #min: Returns the
|
185
|
-
# - #max: Returns the
|
186
|
-
# - #minmax: Returns the
|
186
|
+
# - #min: Returns one or more of the lowest numbers in the set.
|
187
|
+
# - #max: Returns one or more of the highest numbers in the set.
|
188
|
+
# - #minmax: Returns the lowest and highest numbers in the set.
|
187
189
|
#
|
188
|
-
# <i>Accessing value by offset:</i>
|
190
|
+
# <i>Accessing value by offset in sorted set:</i>
|
189
191
|
# - #[] (aliased as #slice): Returns the number or consecutive subset at a
|
190
|
-
# given offset or range of offsets.
|
191
|
-
# - #at: Returns the number at a given offset.
|
192
|
-
# - #find_index: Returns the given number's offset in the set
|
192
|
+
# given offset or range of offsets in the sorted set.
|
193
|
+
# - #at: Returns the number at a given offset in the sorted set.
|
194
|
+
# - #find_index: Returns the given number's offset in the sorted set.
|
195
|
+
#
|
196
|
+
# <i>Accessing value by offset in ordered entries</i>
|
197
|
+
# - #ordered_at: Returns the number at a given offset in the ordered entries.
|
198
|
+
# - #find_ordered_index: Returns the index of the given number's first
|
199
|
+
# occurrence in entries.
|
193
200
|
#
|
194
201
|
# <i>Set cardinality:</i>
|
195
202
|
# - #count (aliased as #size): Returns the count of numbers in the set.
|
203
|
+
# Duplicated numbers are not counted.
|
196
204
|
# - #empty?: Returns whether the set has no members. \IMAP syntax does not
|
197
205
|
# allow empty sequence sets.
|
198
206
|
# - #valid?: Returns whether the set has any members.
|
199
207
|
# - #full?: Returns whether the set contains every possible value, including
|
200
208
|
# <tt>*</tt>.
|
201
209
|
#
|
210
|
+
# <i>Denormalized properties:</i>
|
211
|
+
# - #has_duplicates?: Returns whether the ordered entries repeat any
|
212
|
+
# numbers.
|
213
|
+
# - #count_duplicates: Returns the count of repeated numbers in the ordered
|
214
|
+
# entries.
|
215
|
+
# - #count_with_duplicates: Returns the count of numbers in the ordered
|
216
|
+
# entries, including any repeated numbers.
|
217
|
+
#
|
202
218
|
# === Methods for Iterating
|
203
219
|
#
|
220
|
+
# <i>Normalized (sorted and coalesced):</i>
|
204
221
|
# - #each_element: Yields each number and range in the set, sorted and
|
205
222
|
# coalesced, and returns +self+.
|
206
223
|
# - #elements (aliased as #to_a): Returns an Array of every number and range
|
207
224
|
# in the set, sorted and coalesced.
|
208
|
-
# - #each_entry: Yields each number and range in the set, unsorted and
|
209
|
-
# without deduplicating numbers or coalescing ranges, and returns +self+.
|
210
|
-
# - #entries: Returns an Array of every number and range in the set,
|
211
|
-
# unsorted and without deduplicating numbers or coalescing ranges.
|
212
225
|
# - #each_range:
|
213
226
|
# Yields each element in the set as a Range and returns +self+.
|
214
227
|
# - #ranges: Returns an Array of every element in the set, converting
|
@@ -218,47 +231,70 @@ module Net
|
|
218
231
|
# ranges into all of their contained numbers.
|
219
232
|
# - #to_set: Returns a Set containing all of the #numbers in the set.
|
220
233
|
#
|
234
|
+
# <i>Order preserving:</i>
|
235
|
+
# - #each_entry: Yields each number and range in the set, unsorted and
|
236
|
+
# without deduplicating numbers or coalescing ranges, and returns +self+.
|
237
|
+
# - #entries: Returns an Array of every number and range in the set,
|
238
|
+
# unsorted and without deduplicating numbers or coalescing ranges.
|
239
|
+
# - #each_ordered_number: Yields each number in the ordered entries and
|
240
|
+
# returns +self+.
|
241
|
+
#
|
221
242
|
# === Methods for \Set Operations
|
222
243
|
# These methods do not modify +self+.
|
223
244
|
#
|
224
245
|
# - #| (aliased as #union and #+): Returns a new set combining all members
|
225
|
-
# from +self+ with all members from the other
|
246
|
+
# from +self+ with all members from the other set.
|
226
247
|
# - #& (aliased as #intersection): Returns a new set containing all members
|
227
|
-
# common to +self+ and the other
|
248
|
+
# common to +self+ and the other set.
|
228
249
|
# - #- (aliased as #difference): Returns a copy of +self+ with all members
|
229
|
-
# in the other
|
250
|
+
# in the other set removed.
|
230
251
|
# - #^ (aliased as #xor): Returns a new set containing all members from
|
231
|
-
# +self+ and the other
|
252
|
+
# +self+ and the other set except those common to both.
|
232
253
|
# - #~ (aliased as #complement): Returns a new set containing all members
|
233
254
|
# that are not in +self+
|
255
|
+
# - #above: Return a copy of +self+ which only contains numbers above a
|
256
|
+
# given number.
|
257
|
+
# - #below: Return a copy of +self+ which only contains numbers below a
|
258
|
+
# given value.
|
234
259
|
# - #limit: Returns a copy of +self+ which has replaced <tt>*</tt> with a
|
235
260
|
# given maximum value and removed all members over that maximum.
|
236
261
|
#
|
237
262
|
# === Methods for Assigning
|
238
263
|
# These methods add or replace elements in +self+.
|
239
264
|
#
|
240
|
-
#
|
241
|
-
#
|
265
|
+
# <i>Normalized (sorted and coalesced):</i>
|
266
|
+
#
|
267
|
+
# These methods always update #string to be fully sorted and coalesced.
|
268
|
+
#
|
269
|
+
# - #add (aliased as #<<): Adds a given element to the set; returns +self+.
|
270
|
+
# - #add?: If the given element is not fully included the set, adds it and
|
242
271
|
# returns +self+; otherwise, returns +nil+.
|
243
|
-
# - #merge:
|
244
|
-
# - #
|
272
|
+
# - #merge: Adds all members of the given sets into this set; returns +self+.
|
273
|
+
# - #complement!: Replaces the contents of the set with its own #complement.
|
274
|
+
#
|
275
|
+
# <i>Order preserving:</i>
|
276
|
+
#
|
277
|
+
# These methods _may_ cause #string to not be sorted or coalesced.
|
278
|
+
#
|
279
|
+
# - #append: Adds the given entry to the set, appending it to the existing
|
245
280
|
# string, and returns +self+.
|
246
281
|
# - #string=: Assigns a new #string value and replaces #elements to match.
|
247
282
|
# - #replace: Replaces the contents of the set with the contents
|
248
283
|
# of a given object.
|
249
|
-
# - #complement!: Replaces the contents of the set with its own #complement.
|
250
284
|
#
|
251
285
|
# === Methods for Deleting
|
252
|
-
# These methods remove elements from +self
|
286
|
+
# These methods remove elements from +self+, and update #string to be fully
|
287
|
+
# sorted and coalesced.
|
253
288
|
#
|
254
289
|
# - #clear: Removes all elements in the set; returns +self+.
|
255
|
-
# - #delete: Removes a given
|
256
|
-
# - #delete?: If the given
|
290
|
+
# - #delete: Removes a given element from the set; returns +self+.
|
291
|
+
# - #delete?: If the given element is included in the set, removes it and
|
257
292
|
# returns it; otherwise, returns +nil+.
|
258
293
|
# - #delete_at: Removes the number at a given offset.
|
259
294
|
# - #slice!: Removes the number or consecutive numbers at a given offset or
|
260
295
|
# range of offsets.
|
261
|
-
# - #subtract: Removes
|
296
|
+
# - #subtract: Removes all members of the given sets from this set; returns
|
297
|
+
# +self+.
|
262
298
|
# - #limit!: Replaces <tt>*</tt> with a given maximum value and removes all
|
263
299
|
# members over that maximum; returns +self+.
|
264
300
|
#
|
@@ -286,25 +322,24 @@ module Net
|
|
286
322
|
|
287
323
|
# valid inputs for "*"
|
288
324
|
STARS = [:*, ?*, -1].freeze
|
289
|
-
private_constant :
|
290
|
-
|
291
|
-
COERCIBLE = ->{ _1.respond_to? :to_sequence_set }
|
292
|
-
ENUMABLE = ->{ _1.respond_to?(:each) && _1.respond_to?(:empty?) }
|
293
|
-
private_constant :COERCIBLE, :ENUMABLE
|
325
|
+
private_constant :STARS
|
294
326
|
|
295
327
|
class << self
|
296
328
|
|
297
329
|
# :call-seq:
|
298
|
-
# SequenceSet[*
|
330
|
+
# SequenceSet[*inputs] -> valid frozen sequence set
|
299
331
|
#
|
300
|
-
# Returns a frozen SequenceSet, constructed from +
|
332
|
+
# Returns a frozen SequenceSet, constructed from +inputs+.
|
333
|
+
#
|
334
|
+
# When only a single valid frozen SequenceSet is given, that same set is
|
335
|
+
# returned.
|
301
336
|
#
|
302
337
|
# An empty SequenceSet is invalid and will raise a DataFormatError.
|
303
338
|
#
|
304
339
|
# Use ::new to create a mutable or empty SequenceSet.
|
305
340
|
def [](first, *rest)
|
306
341
|
if rest.empty?
|
307
|
-
if first.is_a?(SequenceSet) &&
|
342
|
+
if first.is_a?(SequenceSet) && first.frozen? && first.valid?
|
308
343
|
first
|
309
344
|
else
|
310
345
|
new(first).validate.freeze
|
@@ -325,7 +360,7 @@ module Net
|
|
325
360
|
# raised.
|
326
361
|
def try_convert(obj)
|
327
362
|
return obj if obj.is_a?(SequenceSet)
|
328
|
-
return nil unless respond_to?(:to_sequence_set)
|
363
|
+
return nil unless obj.respond_to?(:to_sequence_set)
|
329
364
|
obj = obj.to_sequence_set
|
330
365
|
return obj if obj.is_a?(SequenceSet)
|
331
366
|
raise DataFormatError, "invalid object returned from to_sequence_set"
|
@@ -389,6 +424,10 @@ module Net
|
|
389
424
|
# Related: #valid_string, #normalized_string, #to_s
|
390
425
|
def string; @string ||= normalized_string if valid? end
|
391
426
|
|
427
|
+
# Returns an array with #normalized_string when valid and an empty array
|
428
|
+
# otherwise.
|
429
|
+
def deconstruct; valid? ? [normalized_string] : [] end
|
430
|
+
|
392
431
|
# Assigns a new string to #string and resets #elements to match. It
|
393
432
|
# cannot be set to an empty string—assign +nil+ or use #clear instead.
|
394
433
|
# The string is validated but not normalized.
|
@@ -536,26 +575,52 @@ module Net
|
|
536
575
|
empty? || input_to_tuples(other).none? { intersect_tuple? _1 }
|
537
576
|
end
|
538
577
|
|
539
|
-
# :call-seq:
|
578
|
+
# :call-seq:
|
579
|
+
# max(star: :*) => integer or star or nil
|
580
|
+
# max(count, star: :*) => SequenceSet
|
540
581
|
#
|
541
582
|
# Returns the maximum value in +self+, +star+ when the set includes
|
542
583
|
# <tt>*</tt>, or +nil+ when the set is empty.
|
543
|
-
|
544
|
-
|
584
|
+
#
|
585
|
+
# When +count+ is given, a new SequenceSet is returned, containing only
|
586
|
+
# the last +count+ numbers. An empty SequenceSet is returned when +self+
|
587
|
+
# is empty. (+star+ is ignored when +count+ is given.)
|
588
|
+
#
|
589
|
+
# Related: #min, #minmax, #slice
|
590
|
+
def max(count = nil, star: :*)
|
591
|
+
if count
|
592
|
+
slice(-[count, size].min..) || remain_frozen_empty
|
593
|
+
elsif (val = @tuples.last&.last)
|
594
|
+
val == STAR_INT ? star : val
|
595
|
+
end
|
545
596
|
end
|
546
597
|
|
547
|
-
# :call-seq:
|
598
|
+
# :call-seq:
|
599
|
+
# min(star: :*) => integer or star or nil
|
600
|
+
# min(count, star: :*) => SequenceSet
|
548
601
|
#
|
549
602
|
# Returns the minimum value in +self+, +star+ when the only value in the
|
550
603
|
# set is <tt>*</tt>, or +nil+ when the set is empty.
|
551
|
-
|
552
|
-
|
604
|
+
#
|
605
|
+
# When +count+ is given, a new SequenceSet is returned, containing only
|
606
|
+
# the first +count+ numbers. An empty SequenceSet is returned when +self+
|
607
|
+
# is empty. (+star+ is ignored when +count+ is given.)
|
608
|
+
#
|
609
|
+
# Related: #max, #minmax, #slice
|
610
|
+
def min(count = nil, star: :*)
|
611
|
+
if count
|
612
|
+
slice(0...count) || remain_frozen_empty
|
613
|
+
elsif (val = @tuples.first&.first)
|
614
|
+
val != STAR_INT ? val : star
|
615
|
+
end
|
553
616
|
end
|
554
617
|
|
555
618
|
# :call-seq: minmax(star: :*) => nil or [integer, integer or star]
|
556
619
|
#
|
557
620
|
# Returns a 2-element array containing the minimum and maximum numbers in
|
558
621
|
# +self+, or +nil+ when the set is empty.
|
622
|
+
#
|
623
|
+
# Related: #min, #max
|
559
624
|
def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end
|
560
625
|
|
561
626
|
# Returns false when the set is empty.
|
@@ -582,7 +647,14 @@ module Net
|
|
582
647
|
# Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
|
583
648
|
# #=> Net::IMAP::SequenceSet["1:6,99"]
|
584
649
|
#
|
585
|
-
# Related: #add, #merge
|
650
|
+
# Related: #add, #merge, #&, #-, #^, #~
|
651
|
+
#
|
652
|
+
# ==== Set identities
|
653
|
+
#
|
654
|
+
# <tt>lhs | rhs</tt> is equivalent to:
|
655
|
+
# * <tt>rhs | lhs</tt> (commutative)
|
656
|
+
# * <tt>~(~lhs & ~rhs)</tt> (De Morgan's Law)
|
657
|
+
# * <tt>(lhs & rhs) ^ (lhs ^ rhs)</tt>
|
586
658
|
def |(other) remain_frozen dup.merge other end
|
587
659
|
alias :+ :|
|
588
660
|
alias union :|
|
@@ -601,7 +673,17 @@ module Net
|
|
601
673
|
# Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
|
602
674
|
# #=> Net::IMAP::SequenceSet["1,3,5"]
|
603
675
|
#
|
604
|
-
# Related: #subtract
|
676
|
+
# Related: #subtract, #|, #&, #^, #~
|
677
|
+
#
|
678
|
+
# ==== Set identities
|
679
|
+
#
|
680
|
+
# <tt>lhs - rhs</tt> is equivalent to:
|
681
|
+
# * <tt>~r - ~l</tt>
|
682
|
+
# * <tt>lhs & ~rhs</tt>
|
683
|
+
# * <tt>~(~lhs | rhs)</tt>
|
684
|
+
# * <tt>lhs & (lhs ^ rhs)</tt>
|
685
|
+
# * <tt>lhs ^ (lhs & rhs)</tt>
|
686
|
+
# * <tt>rhs ^ (lhs | rhs)</tt>
|
605
687
|
def -(other) remain_frozen dup.subtract other end
|
606
688
|
alias difference :-
|
607
689
|
|
@@ -619,7 +701,17 @@ module Net
|
|
619
701
|
# Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
|
620
702
|
# #=> Net::IMAP::SequenceSet["2,4"]
|
621
703
|
#
|
622
|
-
#
|
704
|
+
# Related: #intersect?, #|, #-, #^, #~
|
705
|
+
#
|
706
|
+
# ==== Set identities
|
707
|
+
#
|
708
|
+
# <tt>lhs & rhs</tt> is equivalent to:
|
709
|
+
# * <tt>rhs & lhs</tt> (commutative)
|
710
|
+
# * <tt>~(~lhs | ~rhs)</tt> (De Morgan's Law)
|
711
|
+
# * <tt>lhs - ~rhs</tt>
|
712
|
+
# * <tt>lhs - (lhs - rhs)</tt>
|
713
|
+
# * <tt>lhs - (lhs ^ rhs)</tt>
|
714
|
+
# * <tt>lhs ^ (lhs - rhs)</tt>
|
623
715
|
def &(other)
|
624
716
|
remain_frozen dup.subtract SequenceSet.new(other).complement!
|
625
717
|
end
|
@@ -639,9 +731,17 @@ module Net
|
|
639
731
|
# Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
|
640
732
|
# #=> Net::IMAP::SequenceSet["1,3,5:6"]
|
641
733
|
#
|
642
|
-
#
|
643
|
-
#
|
644
|
-
|
734
|
+
# Related: #|, #&, #-, #~
|
735
|
+
#
|
736
|
+
# ==== Set identities
|
737
|
+
#
|
738
|
+
# <tt>lhs ^ rhs</tt> is equivalent to:
|
739
|
+
# * <tt>rhs ^ lhs</tt> (commutative)
|
740
|
+
# * <tt>~lhs ^ ~rhs</tt>
|
741
|
+
# * <tt>(lhs | rhs) - (lhs & rhs)</tt>
|
742
|
+
# * <tt>(lhs - rhs) | (rhs - lhs)</tt>
|
743
|
+
# * <tt>(lhs ^ other) ^ (other ^ rhs)</tt>
|
744
|
+
def ^(other) remain_frozen (dup | other).subtract(self & other) end
|
645
745
|
alias xor :^
|
646
746
|
|
647
747
|
# :call-seq:
|
@@ -658,21 +758,29 @@ module Net
|
|
658
758
|
# ~Net::IMAP::SequenceSet["6:99,223:*"]
|
659
759
|
# #=> Net::IMAP::SequenceSet["1:5,100:222"]
|
660
760
|
#
|
661
|
-
# Related: #complement
|
761
|
+
# Related: #complement!, #|, #&, #-, #^
|
762
|
+
#
|
763
|
+
# ==== Set identities
|
764
|
+
#
|
765
|
+
# <tt>~set</tt> is equivalent to:
|
766
|
+
# * <tt>full - set</tt>, where "full" is Net::IMAP::SequenceSet.full
|
662
767
|
def ~; remain_frozen dup.complement! end
|
663
768
|
alias complement :~
|
664
769
|
|
665
770
|
# :call-seq:
|
666
|
-
# add(
|
771
|
+
# add(element) -> self
|
667
772
|
# self << other -> self
|
668
773
|
#
|
669
774
|
# Adds a range or number to the set and returns +self+.
|
670
775
|
#
|
671
776
|
# #string will be regenerated. Use #merge to add many elements at once.
|
672
777
|
#
|
673
|
-
#
|
674
|
-
|
675
|
-
|
778
|
+
# Use #append to append new elements to #string. See
|
779
|
+
# Net::IMAP@Ordered+and+Normalized+Sets.
|
780
|
+
#
|
781
|
+
# Related: #add?, #merge, #union, #append
|
782
|
+
def add(element)
|
783
|
+
tuple_add input_to_tuple element
|
676
784
|
normalize!
|
677
785
|
end
|
678
786
|
alias << add
|
@@ -681,27 +789,33 @@ module Net
|
|
681
789
|
#
|
682
790
|
# Unlike #add, #merge, or #union, the new value is appended to #string.
|
683
791
|
# This may result in a #string which has duplicates or is out-of-order.
|
684
|
-
|
685
|
-
|
792
|
+
#
|
793
|
+
# See Net::IMAP@Ordered+and+Normalized+Sets.
|
794
|
+
#
|
795
|
+
# Related: #add, #merge, #union
|
796
|
+
def append(entry)
|
797
|
+
modifying!
|
798
|
+
tuple = input_to_tuple entry
|
686
799
|
entry = tuple_to_str tuple
|
800
|
+
string unless empty? # write @string before tuple_add
|
687
801
|
tuple_add tuple
|
688
|
-
@string = -(string ? "#{@string},#{entry}" : entry)
|
802
|
+
@string = -(@string ? "#{@string},#{entry}" : entry)
|
689
803
|
self
|
690
804
|
end
|
691
805
|
|
692
|
-
# :call-seq: add?(
|
806
|
+
# :call-seq: add?(element) -> self or nil
|
693
807
|
#
|
694
808
|
# Adds a range or number to the set and returns +self+. Returns +nil+
|
695
|
-
# when the
|
809
|
+
# when the element is already included in the set.
|
696
810
|
#
|
697
811
|
# #string will be regenerated. Use #merge to add many elements at once.
|
698
812
|
#
|
699
813
|
# Related: #add, #merge, #union, #include?
|
700
|
-
def add?(
|
701
|
-
add
|
814
|
+
def add?(element)
|
815
|
+
add element unless include? element
|
702
816
|
end
|
703
817
|
|
704
|
-
# :call-seq: delete(
|
818
|
+
# :call-seq: delete(element) -> self
|
705
819
|
#
|
706
820
|
# Deletes the given range or number from the set and returns +self+.
|
707
821
|
#
|
@@ -709,8 +823,8 @@ module Net
|
|
709
823
|
# many elements at once.
|
710
824
|
#
|
711
825
|
# Related: #delete?, #delete_at, #subtract, #difference
|
712
|
-
def delete(
|
713
|
-
tuple_subtract input_to_tuple
|
826
|
+
def delete(element)
|
827
|
+
tuple_subtract input_to_tuple element
|
714
828
|
normalize!
|
715
829
|
end
|
716
830
|
|
@@ -746,8 +860,8 @@ module Net
|
|
746
860
|
# #string will be regenerated after deletion.
|
747
861
|
#
|
748
862
|
# Related: #delete, #delete_at, #subtract, #difference, #disjoint?
|
749
|
-
def delete?(
|
750
|
-
tuple = input_to_tuple
|
863
|
+
def delete?(element)
|
864
|
+
tuple = input_to_tuple element
|
751
865
|
if tuple.first == tuple.last
|
752
866
|
return unless include_tuple? tuple
|
753
867
|
tuple_subtract tuple
|
@@ -791,33 +905,31 @@ module Net
|
|
791
905
|
deleted
|
792
906
|
end
|
793
907
|
|
794
|
-
# Merges all of the elements that appear in any of the +
|
908
|
+
# Merges all of the elements that appear in any of the +sets+ into the
|
795
909
|
# set, and returns +self+.
|
796
910
|
#
|
797
|
-
# The +
|
798
|
-
#
|
799
|
-
#
|
800
|
-
# these.
|
911
|
+
# The +sets+ may be any objects that would be accepted by ::new: non-zero
|
912
|
+
# 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
|
913
|
+
# strings, other sequence sets, or enumerables containing any of these.
|
801
914
|
#
|
802
|
-
# #string will be regenerated after all
|
915
|
+
# #string will be regenerated after all sets have been merged.
|
803
916
|
#
|
804
917
|
# Related: #add, #add?, #union
|
805
|
-
def merge(*
|
806
|
-
tuples_add input_to_tuples
|
918
|
+
def merge(*sets)
|
919
|
+
tuples_add input_to_tuples sets
|
807
920
|
normalize!
|
808
921
|
end
|
809
922
|
|
810
|
-
# Removes all of the elements that appear in any of the given +
|
811
|
-
#
|
923
|
+
# Removes all of the elements that appear in any of the given +sets+ from
|
924
|
+
# the set, and returns +self+.
|
812
925
|
#
|
813
|
-
# The +
|
814
|
-
#
|
815
|
-
#
|
816
|
-
# these.
|
926
|
+
# The +sets+ may be any objects that would be accepted by ::new: non-zero
|
927
|
+
# 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
|
928
|
+
# strings, other sequence sets, or enumerables containing any of these.
|
817
929
|
#
|
818
930
|
# Related: #difference
|
819
|
-
def subtract(*
|
820
|
-
tuples_subtract input_to_tuples
|
931
|
+
def subtract(*sets)
|
932
|
+
tuples_subtract input_to_tuples sets
|
821
933
|
normalize!
|
822
934
|
end
|
823
935
|
|
@@ -829,21 +941,21 @@ module Net
|
|
829
941
|
# This is useful when the given order is significant, for example in a
|
830
942
|
# ESEARCH response to IMAP#sort.
|
831
943
|
#
|
944
|
+
# See Net::IMAP@Ordered+and+Normalized+Sets.
|
945
|
+
#
|
832
946
|
# Related: #each_entry, #elements
|
833
947
|
def entries; each_entry.to_a end
|
834
948
|
|
835
949
|
# Returns an array of ranges and integers and <tt>:*</tt>.
|
836
950
|
#
|
837
951
|
# The returned elements are sorted and coalesced, even when the input
|
838
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
952
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
953
|
+
# Net::IMAP@Ordered+and+Normalized+Sets.
|
839
954
|
#
|
840
955
|
# By itself, <tt>*</tt> translates to <tt>:*</tt>. A range containing
|
841
956
|
# <tt>*</tt> translates to an endless range. Use #limit to translate both
|
842
957
|
# cases to a maximum value.
|
843
958
|
#
|
844
|
-
# If the original input was unordered or contains overlapping ranges, the
|
845
|
-
# returned ranges will be ordered and coalesced.
|
846
|
-
#
|
847
959
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
|
848
960
|
# #=> [2, 5..9, 11..12, :*]
|
849
961
|
#
|
@@ -854,15 +966,13 @@ module Net
|
|
854
966
|
# Returns an array of ranges
|
855
967
|
#
|
856
968
|
# The returned elements are sorted and coalesced, even when the input
|
857
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize
|
969
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize,
|
970
|
+
# Net::IMAP@Ordered+and+Normalized+Sets.
|
858
971
|
#
|
859
972
|
# <tt>*</tt> translates to an endless range. By itself, <tt>*</tt>
|
860
973
|
# translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
|
861
974
|
# value.
|
862
975
|
#
|
863
|
-
# The returned ranges will be ordered and coalesced, even when the input
|
864
|
-
# #string is not. <tt>*</tt> will sort last. See #normalize.
|
865
|
-
#
|
866
976
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
|
867
977
|
# #=> [2..2, 5..9, 11..12, :*..]
|
868
978
|
# Net::IMAP::SequenceSet["123,999:*,456:789"].ranges
|
@@ -874,7 +984,7 @@ module Net
|
|
874
984
|
# Returns a sorted array of all of the number values in the sequence set.
|
875
985
|
#
|
876
986
|
# The returned numbers are sorted and de-duplicated, even when the input
|
877
|
-
# #string is not. See #normalize.
|
987
|
+
# #string is not. See #normalize, Net::IMAP@Ordered+and+Normalized+Sets.
|
878
988
|
#
|
879
989
|
# Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
|
880
990
|
# #=> [2, 5, 6, 7, 8, 9, 11, 12]
|
@@ -902,23 +1012,23 @@ module Net
|
|
902
1012
|
# Yields each number or range in #string to the block and returns +self+.
|
903
1013
|
# Returns an enumerator when called without a block.
|
904
1014
|
#
|
905
|
-
# The entries are yielded in the same order they appear in #
|
906
|
-
# sorting, deduplication, or coalescing. When #string is in its
|
1015
|
+
# The entries are yielded in the same order they appear in #string, with
|
1016
|
+
# no sorting, deduplication, or coalescing. When #string is in its
|
907
1017
|
# normalized form, this will yield the same values as #each_element.
|
908
1018
|
#
|
1019
|
+
# See Net::IMAP@Ordered+and+Normalized+Sets.
|
1020
|
+
#
|
909
1021
|
# Related: #entries, #each_element
|
910
1022
|
def each_entry(&block) # :yields: integer or range or :*
|
911
1023
|
return to_enum(__method__) unless block_given?
|
912
|
-
|
913
|
-
@string.split(",").each do yield tuple_to_entry str_to_tuple _1 end
|
914
|
-
self
|
1024
|
+
each_entry_tuple do yield tuple_to_entry _1 end
|
915
1025
|
end
|
916
1026
|
|
917
1027
|
# Yields each number or range (or <tt>:*</tt>) in #elements to the block
|
918
1028
|
# and returns self. Returns an enumerator when called without a block.
|
919
1029
|
#
|
920
1030
|
# The returned numbers are sorted and de-duplicated, even when the input
|
921
|
-
# #string is not. See #normalize.
|
1031
|
+
# #string is not. See #normalize, Net::IMAP@Ordered+and+Normalized+Sets.
|
922
1032
|
#
|
923
1033
|
# Related: #elements, #each_entry
|
924
1034
|
def each_element # :yields: integer or range or :*
|
@@ -929,6 +1039,16 @@ module Net
|
|
929
1039
|
|
930
1040
|
private
|
931
1041
|
|
1042
|
+
def each_entry_tuple(&block)
|
1043
|
+
return to_enum(__method__) unless block_given?
|
1044
|
+
if @string
|
1045
|
+
@string.split(",") do block.call str_to_tuple _1 end
|
1046
|
+
else
|
1047
|
+
@tuples.each(&block)
|
1048
|
+
end
|
1049
|
+
self
|
1050
|
+
end
|
1051
|
+
|
932
1052
|
def tuple_to_entry((min, max))
|
933
1053
|
if min == STAR_INT then :*
|
934
1054
|
elsif max == STAR_INT then min..
|
@@ -960,19 +1080,36 @@ module Net
|
|
960
1080
|
# Returns an enumerator when called without a block (even if the set
|
961
1081
|
# contains <tt>*</tt>).
|
962
1082
|
#
|
963
|
-
# Related: #numbers
|
1083
|
+
# Related: #numbers, #each_ordered_number
|
964
1084
|
def each_number(&block) # :yields: integer
|
965
1085
|
return to_enum(__method__) unless block_given?
|
966
1086
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
967
|
-
|
968
|
-
case elem
|
969
|
-
when Range then elem.each(&block)
|
970
|
-
when Integer then block.(elem)
|
971
|
-
end
|
972
|
-
end
|
1087
|
+
@tuples.each do each_number_in_tuple _1, _2, &block end
|
973
1088
|
self
|
974
1089
|
end
|
975
1090
|
|
1091
|
+
# Yields each number in #entries to the block and returns self.
|
1092
|
+
# If the set contains a <tt>*</tt>, RangeError will be raised.
|
1093
|
+
#
|
1094
|
+
# Returns an enumerator when called without a block (even if the set
|
1095
|
+
# contains <tt>*</tt>).
|
1096
|
+
#
|
1097
|
+
# Related: #entries, #each_number
|
1098
|
+
def each_ordered_number(&block)
|
1099
|
+
return to_enum(__method__) unless block_given?
|
1100
|
+
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
1101
|
+
each_entry_tuple do each_number_in_tuple _1, _2, &block end
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
private def each_number_in_tuple(min, max, &block)
|
1105
|
+
if min == STAR_INT then yield :*
|
1106
|
+
elsif min == max then yield min
|
1107
|
+
elsif max != STAR_INT then (min..max).each(&block)
|
1108
|
+
else
|
1109
|
+
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
1110
|
+
end
|
1111
|
+
end
|
1112
|
+
|
976
1113
|
# Returns a Set with all of the #numbers in the sequence set.
|
977
1114
|
#
|
978
1115
|
# If the set contains a <tt>*</tt>, RangeError will be raised.
|
@@ -984,8 +1121,10 @@ module Net
|
|
984
1121
|
|
985
1122
|
# Returns the count of #numbers in the set.
|
986
1123
|
#
|
987
|
-
#
|
988
|
-
# integer value)
|
1124
|
+
# <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
|
1125
|
+
# unsigned integer value).
|
1126
|
+
#
|
1127
|
+
# Related: #count_with_duplicates
|
989
1128
|
def count
|
990
1129
|
@tuples.sum(@tuples.count) { _2 - _1 } +
|
991
1130
|
(include_star? && include?(UINT32_MAX) ? -1 : 0)
|
@@ -993,33 +1132,87 @@ module Net
|
|
993
1132
|
|
994
1133
|
alias size count
|
995
1134
|
|
996
|
-
# Returns the
|
997
|
-
#
|
1135
|
+
# Returns the count of numbers in the ordered #entries, including any
|
1136
|
+
# repeated numbers.
|
1137
|
+
#
|
1138
|
+
# <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
|
1139
|
+
# unsigned integer value).
|
1140
|
+
#
|
1141
|
+
# When #string is normalized, this behaves the same as #count.
|
1142
|
+
#
|
1143
|
+
# Related: #entries, #count_duplicates, #has_duplicates?
|
1144
|
+
def count_with_duplicates
|
1145
|
+
return count unless @string
|
1146
|
+
each_entry_tuple.sum {|min, max|
|
1147
|
+
max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
|
1148
|
+
}
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
# Returns the count of repeated numbers in the ordered #entries, the
|
1152
|
+
# difference between #count_with_duplicates and #count.
|
1153
|
+
#
|
1154
|
+
# When #string is normalized, this is zero.
|
1155
|
+
#
|
1156
|
+
# Related: #entries, #count_with_duplicates, #has_duplicates?
|
1157
|
+
def count_duplicates
|
1158
|
+
return 0 unless @string
|
1159
|
+
count_with_duplicates - count
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
# :call-seq: has_duplicates? -> true | false
|
1163
|
+
#
|
1164
|
+
# Returns whether or not the ordered #entries repeat any numbers.
|
998
1165
|
#
|
999
|
-
#
|
1166
|
+
# Always returns +false+ when #string is normalized.
|
1167
|
+
#
|
1168
|
+
# Related: #entries, #count_with_duplicates, #count_duplicates?
|
1169
|
+
def has_duplicates?
|
1170
|
+
return false unless @string
|
1171
|
+
count_with_duplicates != count
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
# Returns the (sorted and deduplicated) index of +number+ in the set, or
|
1175
|
+
# +nil+ if +number+ isn't in the set.
|
1176
|
+
#
|
1177
|
+
# Related: #[], #at, #find_ordered_index
|
1000
1178
|
def find_index(number)
|
1001
1179
|
number = to_tuple_int number
|
1002
|
-
each_tuple_with_index do |min, max, idx_min|
|
1180
|
+
each_tuple_with_index(@tuples) do |min, max, idx_min|
|
1003
1181
|
number < min and return nil
|
1004
1182
|
number <= max and return from_tuple_int(idx_min + (number - min))
|
1005
1183
|
end
|
1006
1184
|
nil
|
1007
1185
|
end
|
1008
1186
|
|
1187
|
+
# Returns the first index of +number+ in the ordered #entries, or
|
1188
|
+
# +nil+ if +number+ isn't in the set.
|
1189
|
+
#
|
1190
|
+
# Related: #find_index
|
1191
|
+
def find_ordered_index(number)
|
1192
|
+
number = to_tuple_int number
|
1193
|
+
each_tuple_with_index(each_entry_tuple) do |min, max, idx_min|
|
1194
|
+
if min <= number && number <= max
|
1195
|
+
return from_tuple_int(idx_min + (number - min))
|
1196
|
+
end
|
1197
|
+
end
|
1198
|
+
nil
|
1199
|
+
end
|
1200
|
+
|
1009
1201
|
private
|
1010
1202
|
|
1011
|
-
def each_tuple_with_index
|
1203
|
+
def each_tuple_with_index(tuples)
|
1012
1204
|
idx_min = 0
|
1013
|
-
|
1014
|
-
|
1205
|
+
tuples.each do |min, max|
|
1206
|
+
idx_max = idx_min + (max - min)
|
1207
|
+
yield min, max, idx_min, idx_max
|
1015
1208
|
idx_min = idx_max + 1
|
1016
1209
|
end
|
1017
1210
|
idx_min
|
1018
1211
|
end
|
1019
1212
|
|
1020
|
-
def reverse_each_tuple_with_index
|
1213
|
+
def reverse_each_tuple_with_index(tuples)
|
1021
1214
|
idx_max = -1
|
1022
|
-
|
1215
|
+
tuples.reverse_each do |min, max|
|
1023
1216
|
yield min, max, (idx_min = idx_max - (max - min)), idx_max
|
1024
1217
|
idx_max = idx_min - 1
|
1025
1218
|
end
|
@@ -1030,18 +1223,38 @@ module Net
|
|
1030
1223
|
|
1031
1224
|
# :call-seq: at(index) -> integer or nil
|
1032
1225
|
#
|
1033
|
-
# Returns
|
1034
|
-
#
|
1226
|
+
# Returns the number at the given +index+ in the sorted set, without
|
1227
|
+
# modifying the set.
|
1035
1228
|
#
|
1036
|
-
#
|
1229
|
+
# +index+ is interpreted the same as in #[], except that #at only allows a
|
1230
|
+
# single integer argument.
|
1231
|
+
#
|
1232
|
+
# Related: #[], #slice, #ordered_at
|
1037
1233
|
def at(index)
|
1234
|
+
lookup_number_by_tuple_index(tuples, index)
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
# :call-seq: ordered_at(index) -> integer or nil
|
1238
|
+
#
|
1239
|
+
# Returns the number at the given +index+ in the ordered #entries, without
|
1240
|
+
# modifying the set.
|
1241
|
+
#
|
1242
|
+
# +index+ is interpreted the same as in #at (and #[]), except that
|
1243
|
+
# #ordered_at applies to the ordered #entries, not the sorted set.
|
1244
|
+
#
|
1245
|
+
# Related: #[], #slice, #ordered_at
|
1246
|
+
def ordered_at(index)
|
1247
|
+
lookup_number_by_tuple_index(each_entry_tuple, index)
|
1248
|
+
end
|
1249
|
+
|
1250
|
+
private def lookup_number_by_tuple_index(tuples, index)
|
1038
1251
|
index = Integer(index.to_int)
|
1039
1252
|
if index.negative?
|
1040
|
-
reverse_each_tuple_with_index do |min, max, idx_min, idx_max|
|
1253
|
+
reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
|
1041
1254
|
idx_min <= index and return from_tuple_int(min + (index - idx_min))
|
1042
1255
|
end
|
1043
1256
|
else
|
1044
|
-
each_tuple_with_index do |min, _, idx_min, idx_max|
|
1257
|
+
each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
|
1045
1258
|
index <= idx_max and return from_tuple_int(min + (index - idx_min))
|
1046
1259
|
end
|
1047
1260
|
end
|
@@ -1056,17 +1269,18 @@ module Net
|
|
1056
1269
|
# seqset[range] -> sequence set or nil
|
1057
1270
|
# slice(range) -> sequence set or nil
|
1058
1271
|
#
|
1059
|
-
# Returns a number or a subset from
|
1272
|
+
# Returns a number or a subset from the _sorted_ set, without modifying
|
1273
|
+
# the set.
|
1060
1274
|
#
|
1061
1275
|
# When an Integer argument +index+ is given, the number at offset +index+
|
1062
|
-
# is returned:
|
1276
|
+
# in the sorted set is returned:
|
1063
1277
|
#
|
1064
1278
|
# set = Net::IMAP::SequenceSet["10:15,20:23,26"]
|
1065
1279
|
# set[0] #=> 10
|
1066
1280
|
# set[5] #=> 15
|
1067
1281
|
# set[10] #=> 26
|
1068
1282
|
#
|
1069
|
-
# If +index+ is negative, it counts relative to the end of
|
1283
|
+
# If +index+ is negative, it counts relative to the end of the sorted set:
|
1070
1284
|
# set = Net::IMAP::SequenceSet["10:15,20:23,26"]
|
1071
1285
|
# set[-1] #=> 26
|
1072
1286
|
# set[-3] #=> 22
|
@@ -1078,13 +1292,14 @@ module Net
|
|
1078
1292
|
# set[11] #=> nil
|
1079
1293
|
# set[-12] #=> nil
|
1080
1294
|
#
|
1081
|
-
# The result is based on the
|
1082
|
-
#
|
1295
|
+
# The result is based on the sorted and de-duplicated set, not on the
|
1296
|
+
# ordered #entries in #string.
|
1083
1297
|
#
|
1084
1298
|
# set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
|
1085
1299
|
# set[0] #=> 11
|
1086
1300
|
# set[-1] #=> 23
|
1087
1301
|
#
|
1302
|
+
# Related: #at
|
1088
1303
|
def [](index, length = nil)
|
1089
1304
|
if length then slice_length(index, length)
|
1090
1305
|
elsif index.is_a?(Range) then slice_range(index)
|
@@ -1107,20 +1322,76 @@ module Net
|
|
1107
1322
|
def slice_range(range)
|
1108
1323
|
first = range.begin || 0
|
1109
1324
|
last = range.end || -1
|
1110
|
-
|
1325
|
+
if range.exclude_end?
|
1326
|
+
return remain_frozen_empty if last.zero?
|
1327
|
+
last -= 1 if range.end && last != STAR_INT
|
1328
|
+
end
|
1111
1329
|
if (first * last).positive? && last < first
|
1112
|
-
|
1330
|
+
remain_frozen_empty
|
1113
1331
|
elsif (min = at(first))
|
1114
1332
|
max = at(last)
|
1333
|
+
max = :* if max.nil?
|
1115
1334
|
if max == :* then self & (min..)
|
1116
1335
|
elsif min <= max then self & (min..max)
|
1117
|
-
else
|
1336
|
+
else remain_frozen_empty
|
1118
1337
|
end
|
1119
1338
|
end
|
1120
1339
|
end
|
1121
1340
|
|
1122
1341
|
public
|
1123
1342
|
|
1343
|
+
# Returns a copy of +self+ which only contains the numbers above +num+.
|
1344
|
+
#
|
1345
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
|
1346
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(20) # to_s => "21:22,50
|
1347
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].above(30) # to_s => "50"
|
1348
|
+
#
|
1349
|
+
# This returns the same result as #intersection with <tt>((num+1)..)</tt>
|
1350
|
+
# or #difference with <tt>(..num)</tt>.
|
1351
|
+
#
|
1352
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (11..) # to_s => "11:22,50"
|
1353
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..10) # to_s => "11:22,50"
|
1354
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (21..) # to_s => "21:22,50"
|
1355
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (..20) # to_s => "21:22,50"
|
1356
|
+
#
|
1357
|
+
# Related: #above, #-, #&
|
1358
|
+
def above(num)
|
1359
|
+
NumValidator.valid_nz_number?(num) or
|
1360
|
+
raise ArgumentError, "not a valid sequence set number"
|
1361
|
+
difference(..num)
|
1362
|
+
end
|
1363
|
+
|
1364
|
+
# Returns a copy of +self+ which only contains numbers below +num+.
|
1365
|
+
#
|
1366
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(10) # to_s => "5"
|
1367
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(20) # to_s => "5,10:19"
|
1368
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
1369
|
+
#
|
1370
|
+
# This returns the same result as #intersection with <tt>(..(num-1))</tt>
|
1371
|
+
# or #difference with <tt>(num..)</tt>.
|
1372
|
+
#
|
1373
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..9) # to_s => "5"
|
1374
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (10..) # to_s => "5"
|
1375
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] & (..19) # to_s => "5,10:19"
|
1376
|
+
# Net::IMAP::SequenceSet["5,10:22,50"] - (20..) # to_s => "5,10:19"
|
1377
|
+
#
|
1378
|
+
# When the set does not contain <tt>*</tt>, #below is identical to #limit
|
1379
|
+
# with <tt>max: num - 1</tt>. When the set does contain <tt>*</tt>,
|
1380
|
+
# #below always drops it from the result. Use #limit when the IMAP
|
1381
|
+
# semantics for <tt>*</tt> must be enforced.
|
1382
|
+
#
|
1383
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
|
1384
|
+
# Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 29) # to_s => "5,10:22"
|
1385
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].below(30) # to_s => "5,10:22"
|
1386
|
+
# Net::IMAP::SequenceSet["5,10:22,*"].limit(max: 29) # to_s => "5,10:22,29"
|
1387
|
+
#
|
1388
|
+
# Related: #above, #-, #&, #limit
|
1389
|
+
def below(num)
|
1390
|
+
NumValidator.valid_nz_number?(num) or
|
1391
|
+
raise ArgumentError, "not a valid sequence set number"
|
1392
|
+
difference(num..)
|
1393
|
+
end
|
1394
|
+
|
1124
1395
|
# Returns a frozen SequenceSet with <tt>*</tt> converted to +max+, numbers
|
1125
1396
|
# and ranges over +max+ removed, and ranges containing +max+ converted to
|
1126
1397
|
# end at +max+.
|
@@ -1138,6 +1409,7 @@ module Net
|
|
1138
1409
|
# Net::IMAP::SequenceSet["500:*"].limit(max: 37)
|
1139
1410
|
# #=> Net::IMAP::SequenceSet["37"]
|
1140
1411
|
#
|
1412
|
+
# Related: #limit!
|
1141
1413
|
def limit(max:)
|
1142
1414
|
max = to_tuple_int(max)
|
1143
1415
|
if empty? then self.class.empty
|
@@ -1179,6 +1451,7 @@ module Net
|
|
1179
1451
|
#
|
1180
1452
|
# The returned set's #string is sorted and deduplicated. Adjacent or
|
1181
1453
|
# overlapping elements will be merged into a single larger range.
|
1454
|
+
# See Net::IMAP@Ordered+and+Normalized+Sets.
|
1182
1455
|
#
|
1183
1456
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
|
1184
1457
|
# #=> Net::IMAP::SequenceSet["1:7,9:11"]
|
@@ -1191,7 +1464,7 @@ module Net
|
|
1191
1464
|
end
|
1192
1465
|
|
1193
1466
|
# Resets #string to be sorted, deduplicated, and coalesced. Returns
|
1194
|
-
# +self+.
|
1467
|
+
# +self+. See Net::IMAP@Ordered+and+Normalized+Sets.
|
1195
1468
|
#
|
1196
1469
|
# Related: #normalize, #normalized_string
|
1197
1470
|
def normalize!
|
@@ -1201,11 +1474,13 @@ module Net
|
|
1201
1474
|
|
1202
1475
|
# Returns a normalized +sequence-set+ string representation, sorted
|
1203
1476
|
# and deduplicated. Adjacent or overlapping elements will be merged into
|
1204
|
-
# a single larger range.
|
1477
|
+
# a single larger range. See Net::IMAP@Ordered+and+Normalized+Sets.
|
1205
1478
|
#
|
1206
1479
|
# Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
|
1207
1480
|
# #=> "1:7,9:11"
|
1208
1481
|
#
|
1482
|
+
# Returns +nil+ when the set is empty.
|
1483
|
+
#
|
1209
1484
|
# Related: #normalize!, #normalize
|
1210
1485
|
def normalized_string
|
1211
1486
|
@tuples.empty? ? nil : -@tuples.map { tuple_to_str _1 }.join(",")
|
@@ -1235,6 +1510,18 @@ module Net
|
|
1235
1510
|
imap.__send__(:put_string, valid_string)
|
1236
1511
|
end
|
1237
1512
|
|
1513
|
+
# For YAML serialization
|
1514
|
+
def encode_with(coder) # :nodoc:
|
1515
|
+
# we can perfectly reconstruct from the string
|
1516
|
+
coder['string'] = to_s
|
1517
|
+
end
|
1518
|
+
|
1519
|
+
# For YAML deserialization
|
1520
|
+
def init_with(coder) # :nodoc:
|
1521
|
+
@tuples = []
|
1522
|
+
self.string = coder['string']
|
1523
|
+
end
|
1524
|
+
|
1238
1525
|
protected
|
1239
1526
|
|
1240
1527
|
attr_reader :tuples # :nodoc:
|
@@ -1242,6 +1529,7 @@ module Net
|
|
1242
1529
|
private
|
1243
1530
|
|
1244
1531
|
def remain_frozen(set) frozen? ? set.freeze : set end
|
1532
|
+
def remain_frozen_empty; frozen? ? SequenceSet.empty : SequenceSet.new end
|
1245
1533
|
|
1246
1534
|
# frozen clones are shallow copied
|
1247
1535
|
def initialize_clone(other)
|
@@ -1254,29 +1542,30 @@ module Net
|
|
1254
1542
|
super
|
1255
1543
|
end
|
1256
1544
|
|
1257
|
-
def input_to_tuple(
|
1258
|
-
|
1259
|
-
case
|
1260
|
-
when *STARS, Integer then [int = to_tuple_int(
|
1261
|
-
when Range then range_to_tuple(
|
1262
|
-
when String then str_to_tuple(
|
1545
|
+
def input_to_tuple(entry)
|
1546
|
+
entry = input_try_convert entry
|
1547
|
+
case entry
|
1548
|
+
when *STARS, Integer then [int = to_tuple_int(entry), int]
|
1549
|
+
when Range then range_to_tuple(entry)
|
1550
|
+
when String then str_to_tuple(entry)
|
1263
1551
|
else
|
1264
|
-
raise DataFormatError, "expected number or range, got %p" % [
|
1552
|
+
raise DataFormatError, "expected number or range, got %p" % [entry]
|
1265
1553
|
end
|
1266
1554
|
end
|
1267
1555
|
|
1268
|
-
def input_to_tuples(
|
1269
|
-
|
1270
|
-
case
|
1271
|
-
when *STARS, Integer, Range then [input_to_tuple(
|
1272
|
-
when String then str_to_tuples
|
1273
|
-
when SequenceSet then
|
1274
|
-
when
|
1556
|
+
def input_to_tuples(set)
|
1557
|
+
set = input_try_convert set
|
1558
|
+
case set
|
1559
|
+
when *STARS, Integer, Range then [input_to_tuple(set)]
|
1560
|
+
when String then str_to_tuples set
|
1561
|
+
when SequenceSet then set.tuples
|
1562
|
+
when Set then set.map { [to_tuple_int(_1)] * 2 }
|
1563
|
+
when Array then set.flat_map { input_to_tuples _1 }
|
1275
1564
|
when nil then []
|
1276
1565
|
else
|
1277
1566
|
raise DataFormatError,
|
1278
1567
|
"expected nz-number, range, string, or enumerable; " \
|
1279
|
-
"got %p" % [
|
1568
|
+
"got %p" % [set]
|
1280
1569
|
end
|
1281
1570
|
end
|
1282
1571
|
|
@@ -1284,8 +1573,7 @@ module Net
|
|
1284
1573
|
# String, Set, Array, or... any type of object.
|
1285
1574
|
def input_try_convert(input)
|
1286
1575
|
SequenceSet.try_convert(input) ||
|
1287
|
-
|
1288
|
-
input.respond_to?(:to_int) && Integer(input.to_int) ||
|
1576
|
+
Integer.try_convert(input) ||
|
1289
1577
|
String.try_convert(input) ||
|
1290
1578
|
input
|
1291
1579
|
end
|
@@ -1317,6 +1605,12 @@ module Net
|
|
1317
1605
|
range.include?(min) || range.include?(max) || (min..max).cover?(range)
|
1318
1606
|
end
|
1319
1607
|
|
1608
|
+
def modifying!
|
1609
|
+
if frozen?
|
1610
|
+
raise FrozenError, "can't modify frozen #{self.class}: %p" % [self]
|
1611
|
+
end
|
1612
|
+
end
|
1613
|
+
|
1320
1614
|
def tuples_add(tuples) tuples.each do tuple_add _1 end; self end
|
1321
1615
|
def tuples_subtract(tuples) tuples.each do tuple_subtract _1 end; self end
|
1322
1616
|
|
@@ -1331,10 +1625,11 @@ module Net
|
|
1331
1625
|
# ---------??===lower==|--|==|----|===upper===|-- join until upper
|
1332
1626
|
# ---------??===lower==|--|==|--|=====upper===|-- join to upper
|
1333
1627
|
def tuple_add(tuple)
|
1628
|
+
modifying!
|
1334
1629
|
min, max = tuple
|
1335
1630
|
lower, lower_idx = tuple_gte_with_index(min - 1)
|
1336
|
-
if lower.nil? then tuples <<
|
1337
|
-
elsif (max + 1) < lower.first then tuples.insert(lower_idx,
|
1631
|
+
if lower.nil? then tuples << [min, max]
|
1632
|
+
elsif (max + 1) < lower.first then tuples.insert(lower_idx, [min, max])
|
1338
1633
|
else tuple_coalesce(lower, lower_idx, min, max)
|
1339
1634
|
end
|
1340
1635
|
end
|
@@ -1367,6 +1662,7 @@ module Net
|
|
1367
1662
|
# -------??=====lower====|--|====|---|====upper====|-- 7. delete until
|
1368
1663
|
# -------??=====lower====|--|====|--|=====upper====|-- 8. delete and trim
|
1369
1664
|
def tuple_subtract(tuple)
|
1665
|
+
modifying!
|
1370
1666
|
min, max = tuple
|
1371
1667
|
lower, idx = tuple_gte_with_index(min)
|
1372
1668
|
if lower.nil? then nil # case 1.
|
@@ -1407,12 +1703,11 @@ module Net
|
|
1407
1703
|
end
|
1408
1704
|
|
1409
1705
|
def nz_number(num)
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
num
|
1706
|
+
String === num && !/\A[1-9]\d*\z/.match?(num) and
|
1707
|
+
raise DataFormatError, "%p is not a valid nz-number" % [num]
|
1708
|
+
NumValidator.ensure_nz_number Integer num
|
1709
|
+
rescue TypeError # To catch errors from Integer()
|
1710
|
+
raise DataFormatError, $!.message
|
1416
1711
|
end
|
1417
1712
|
|
1418
1713
|
# intentionally defined after the class implementation
|