net-imap 0.5.6 → 0.5.9

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.
@@ -18,21 +18,9 @@ module Net
18
18
  #
19
19
  # == Creating sequence sets
20
20
  #
21
- # SequenceSet.new with no arguments creates an empty sequence set. Note
22
- # that an empty sequence set is invalid in the \IMAP grammar.
23
- #
24
- # set = Net::IMAP::SequenceSet.new
25
- # set.empty? #=> true
26
- # set.valid? #=> false
27
- # set.valid_string #!> raises DataFormatError
28
- # set << 1..10
29
- # set.empty? #=> false
30
- # set.valid? #=> true
31
- # set.valid_string #=> "1:10"
32
- #
33
21
  # SequenceSet.new may receive a single optional argument: a non-zero 32 bit
34
22
  # unsigned integer, a range, a <tt>sequence-set</tt> formatted string,
35
- # another sequence set, a Set (containing only numbers or <tt>*</tt>), or an
23
+ # another SequenceSet, a Set (containing only numbers or <tt>*</tt>), or an
36
24
  # Array containing any of these (array inputs may be nested).
37
25
  #
38
26
  # set = Net::IMAP::SequenceSet.new(1)
@@ -48,30 +36,114 @@ module Net
48
36
  # set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
49
37
  # set.valid_string #=> "1:10,55,1024:2048"
50
38
  #
51
- # Use ::[] with one or more arguments to create a frozen SequenceSet. An
52
- # invalid (empty) set cannot be created with ::[].
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
53
67
  #
68
+ # copy << 999
69
+ # copy.valid_string #=> "1:10,999,1024,2048"
70
+ # copy == input #=> false, different set membership
71
+ # copy.eql? input #=> false, different string value
72
+ #
73
+ # Use Net::IMAP::SequenceSet() to coerce a single (optional) input.
74
+ # A SequenceSet input is returned without duplication, even when frozen.
75
+ #
76
+ # set = Net::IMAP::SequenceSet()
77
+ # set.string #=> nil
78
+ # set.frozen? #=> false
79
+ #
80
+ # # String order is preserved
81
+ # set = Net::IMAP::SequenceSet("1,2,3:7,5,6:10,2048,1024")
82
+ # set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
83
+ # set.frozen? #=> false
84
+ #
85
+ # # Other inputs are normalized
86
+ # set = Net::IMAP::SequenceSet([1, 2, [3..7, 5], 6..10, 2048, 1024])
87
+ # set.valid_string #=> "1:10,55,1024:2048"
88
+ # set.frozen? #=> false
89
+ #
90
+ # unfrozen = set
91
+ # frozen = set.dup.freeze
92
+ # unfrozen.equal? Net::IMAP::SequenceSet(unfrozen) #=> true
93
+ # frozen.equal? Net::IMAP::SequenceSet(frozen) #=> true
94
+ #
95
+ # Use ::[] to coerce one or more arguments into a valid frozen SequenceSet.
96
+ # A valid frozen SequenceSet is returned directly, without allocating a new
97
+ # object. ::[] will not create an invalid (empty) set.
98
+ #
99
+ # Net::IMAP::SequenceSet[] #!> raises ArgumentError
100
+ # Net::IMAP::SequenceSet[nil] #!> raises DataFormatError
101
+ # Net::IMAP::SequenceSet[""] #!> raises DataFormatError
102
+ #
103
+ # # String order is preserved
54
104
  # set = Net::IMAP::SequenceSet["1,2,3:7,5,6:10,2048,1024"]
55
105
  # set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
106
+ # set.frozen? #=> true
107
+ #
108
+ # # Other inputs are normalized
56
109
  # set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
57
110
  # set.valid_string #=> "1:10,55,1024:2048"
111
+ # set.frozen? #=> true
112
+ #
113
+ # frozen = set
114
+ # unfrozen = set.dup
115
+ # frozen.equal? Net::IMAP::SequenceSet[frozen] #=> true
116
+ # unfrozen.equal? Net::IMAP::SequenceSet[unfrozen] #=> false
117
+ #
118
+ # Objects which respond to +to_sequence_set+ (such as SearchResult and
119
+ # ThreadMember) can be coerced to a SequenceSet with ::new, ::try_convert,
120
+ # ::[], or Net::IMAP::SequenceSet.
121
+ #
122
+ # search = imap.uid_search(["SUBJECT", "hello", "NOT", "SEEN"])
123
+ # seqset = Net::IMAP::SequenceSet(search) - already_fetched
124
+ # fetch = imap.uid_fetch(seqset, "FAST")
58
125
  #
59
126
  # == Ordered and Normalized sets
60
127
  #
61
128
  # Sometimes the order of the set's members is significant, such as with the
62
129
  # +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
63
- # sequence set is created by the parser or with a single string value, that
64
- # #string representation is preserved.
130
+ # sequence set is created from a single string (such as by the parser), that
131
+ # #string representation is preserved. Assigning a string with #string= or
132
+ # #replace will also preserve that string. Use #each_entry, #entries, or
133
+ # #each_ordered_number to enumerate the entries in their #string order.
134
+ # Hash equality (using #eql?) is based on the string representation.
65
135
  #
66
- # Internally, SequenceSet 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.
136
+ # Internally, SequenceSet uses a normalized uint32 set representation which
137
+ # sorts and de-duplicates all numbers and coalesces adjacent or overlapping
138
+ # entries. Many methods use this sorted set representation for <tt>O(lg
139
+ # n)</tt> searches. Use #each_element, #elements, #each_range, #ranges,
140
+ # #each_number, or #numbers to enumerate the set in sorted order. Basic
141
+ # object equality (using #==) is based on set membership, without regard to
142
+ # #entry order or #string normalization.
71
143
  #
72
- # Most modification methods convert #string to its normalized form. To
73
- # preserve #string order while modifying a set, use #append, #string=, or
74
- # #replace.
144
+ # Most modification methods reset #string to its #normalized form, so that
145
+ # #entries and #elements are identical. Use #append to preserve #entries
146
+ # order while modifying a set.
75
147
  #
76
148
  # == Using <tt>*</tt>
77
149
  #
@@ -108,11 +180,15 @@ module Net
108
180
  # When a set includes <tt>*</tt>, some methods may have surprising behavior.
109
181
  #
110
182
  # For example, #complement treats <tt>*</tt> as its own number. This way,
111
- # the #intersection of a set and its #complement will always be empty.
112
- # This is not how an \IMAP server interprets the set: it will convert
113
- # <tt>*</tt> to either the number of messages in the mailbox or +UIDNEXT+,
114
- # as appropriate. And there _will_ be overlap between a set and its
115
- # complement after #limit is applied to each:
183
+ # the #intersection of a set and its #complement will always be empty. And
184
+ # <tt>*</tt> is sorted as greater than any other number in the set. This is
185
+ # not how an \IMAP server interprets the set: it will convert <tt>*</tt> to
186
+ # the number of messages in the mailbox, the +UID+ of the last message in
187
+ # the mailbox, or +UIDNEXT+, as appropriate. Several methods have an
188
+ # argument for how <tt>*</tt> should be interpreted.
189
+ #
190
+ # But, for example, this means that there may be overlap between a set and
191
+ # its complement after #limit is applied to each:
116
192
  #
117
193
  # ~Net::IMAP::SequenceSet["*"] == Net::IMAP::SequenceSet[1..(2**32-1)]
118
194
  # ~Net::IMAP::SequenceSet[1..5] == Net::IMAP::SequenceSet["6:*"]
@@ -149,6 +225,7 @@ module Net
149
225
  # * ::new: Creates a new mutable sequence set, which may be empty (invalid).
150
226
  # * ::try_convert: Calls +to_sequence_set+ on an object and verifies that
151
227
  # the result is a SequenceSet.
228
+ # * Net::IMAP::SequenceSet(): Coerce an input using ::try_convert or ::new.
152
229
  # * ::empty: Returns a frozen empty (invalid) SequenceSet.
153
230
  # * ::full: Returns a frozen SequenceSet containing every possible number.
154
231
  #
@@ -174,14 +251,13 @@ module Net
174
251
  #
175
252
  # <i>Set membership:</i>
176
253
  # - #include? (aliased as #member?):
177
- # Returns whether a given object (nz-number, range, or <tt>*</tt>) is
178
- # contained by the set.
254
+ # Returns whether a given element is contained by the set.
179
255
  # - #include_star?: Returns whether the set contains <tt>*</tt>.
180
256
  #
181
257
  # <i>Minimum and maximum value elements:</i>
182
- # - #min: Returns the minimum number in the set.
183
- # - #max: Returns the maximum number in the set.
184
- # - #minmax: Returns the minimum and maximum numbers in the set.
258
+ # - #min: Returns one or more of the lowest numbers in the set.
259
+ # - #max: Returns one or more of the highest numbers in the set.
260
+ # - #minmax: Returns the lowest and highest numbers in the set.
185
261
  #
186
262
  # <i>Accessing value by offset in sorted set:</i>
187
263
  # - #[] (aliased as #slice): Returns the number or consecutive subset at a
@@ -239,15 +315,19 @@ module Net
239
315
  # These methods do not modify +self+.
240
316
  #
241
317
  # - #| (aliased as #union and #+): Returns a new set combining all members
242
- # from +self+ with all members from the other object.
318
+ # from +self+ with all members from the other set.
243
319
  # - #& (aliased as #intersection): Returns a new set containing all members
244
- # common to +self+ and the other object.
320
+ # common to +self+ and the other set.
245
321
  # - #- (aliased as #difference): Returns a copy of +self+ with all members
246
- # in the other object removed.
322
+ # in the other set removed.
247
323
  # - #^ (aliased as #xor): Returns a new set containing all members from
248
- # +self+ and the other object except those common to both.
324
+ # +self+ and the other set except those common to both.
249
325
  # - #~ (aliased as #complement): Returns a new set containing all members
250
326
  # that are not in +self+
327
+ # - #above: Return a copy of +self+ which only contains numbers above a
328
+ # given number.
329
+ # - #below: Return a copy of +self+ which only contains numbers below a
330
+ # given value.
251
331
  # - #limit: Returns a copy of +self+ which has replaced <tt>*</tt> with a
252
332
  # given maximum value and removed all members over that maximum.
253
333
  #
@@ -258,17 +338,17 @@ module Net
258
338
  #
259
339
  # These methods always update #string to be fully sorted and coalesced.
260
340
  #
261
- # - #add (aliased as #<<): Adds a given object to the set; returns +self+.
262
- # - #add?: If the given object is not an element in the set, adds it and
341
+ # - #add (aliased as #<<): Adds a given element to the set; returns +self+.
342
+ # - #add?: If the given element is not fully included the set, adds it and
263
343
  # returns +self+; otherwise, returns +nil+.
264
- # - #merge: Merges multiple elements into the set; returns +self+.
344
+ # - #merge: Adds all members of the given sets into this set; returns +self+.
265
345
  # - #complement!: Replaces the contents of the set with its own #complement.
266
346
  #
267
347
  # <i>Order preserving:</i>
268
348
  #
269
349
  # These methods _may_ cause #string to not be sorted or coalesced.
270
350
  #
271
- # - #append: Adds a given object to the set, appending it to the existing
351
+ # - #append: Adds the given entry to the set, appending it to the existing
272
352
  # string, and returns +self+.
273
353
  # - #string=: Assigns a new #string value and replaces #elements to match.
274
354
  # - #replace: Replaces the contents of the set with the contents
@@ -279,13 +359,14 @@ module Net
279
359
  # sorted and coalesced.
280
360
  #
281
361
  # - #clear: Removes all elements in the set; returns +self+.
282
- # - #delete: Removes a given object from the set; returns +self+.
283
- # - #delete?: If the given object is an element in the set, removes it and
362
+ # - #delete: Removes a given element from the set; returns +self+.
363
+ # - #delete?: If the given element is included in the set, removes it and
284
364
  # returns it; otherwise, returns +nil+.
285
365
  # - #delete_at: Removes the number at a given offset.
286
366
  # - #slice!: Removes the number or consecutive numbers at a given offset or
287
367
  # range of offsets.
288
- # - #subtract: Removes each given object from the set; returns +self+.
368
+ # - #subtract: Removes all members of the given sets from this set; returns
369
+ # +self+.
289
370
  # - #limit!: Replaces <tt>*</tt> with a given maximum value and removes all
290
371
  # members over that maximum; returns +self+.
291
372
  #
@@ -318,20 +399,22 @@ module Net
318
399
  class << self
319
400
 
320
401
  # :call-seq:
321
- # SequenceSet[*values] -> valid frozen sequence set
402
+ # SequenceSet[*inputs] -> valid frozen sequence set
403
+ #
404
+ # Returns a frozen SequenceSet, constructed from +inputs+.
322
405
  #
323
- # Returns a frozen SequenceSet, constructed from +values+.
406
+ # When only a single valid frozen SequenceSet is given, that same set is
407
+ # returned.
324
408
  #
325
409
  # An empty SequenceSet is invalid and will raise a DataFormatError.
326
410
  #
327
411
  # Use ::new to create a mutable or empty SequenceSet.
412
+ #
413
+ # Related: ::new, Net::IMAP::SequenceSet(), ::try_convert
328
414
  def [](first, *rest)
329
415
  if rest.empty?
330
- if first.is_a?(SequenceSet) && first.frozen? && first.valid?
331
- first
332
- else
333
- new(first).validate.freeze
334
- end
416
+ set = try_convert(first)&.validate
417
+ set&.frozen? ? set : (set&.dup || new(first).validate).freeze
335
418
  else
336
419
  new(first).merge(*rest).validate.freeze
337
420
  end
@@ -346,6 +429,8 @@ module Net
346
429
  #
347
430
  # If +obj.to_sequence_set+ doesn't return a SequenceSet, an exception is
348
431
  # raised.
432
+ #
433
+ # Related: Net::IMAP::SequenceSet(), ::new, ::[]
349
434
  def try_convert(obj)
350
435
  return obj if obj.is_a?(SequenceSet)
351
436
  return nil unless obj.respond_to?(:to_sequence_set)
@@ -364,20 +449,85 @@ module Net
364
449
  end
365
450
 
366
451
  # Create a new SequenceSet object from +input+, which may be another
367
- # SequenceSet, an IMAP formatted +sequence-set+ string, a number, a
368
- # range, <tt>:*</tt>, or an enumerable of these.
369
- #
370
- # Use ::[] to create a frozen (non-empty) SequenceSet.
452
+ # SequenceSet, an IMAP formatted +sequence-set+ string, a non-zero 32 bit
453
+ # unsigned integer, a range, <tt>:*</tt>, a Set of numbers or <tt>*</tt>,
454
+ # an object that responds to +to_sequence_set+ (such as SearchResult) or
455
+ # an Array of these (array inputs may be nested).
456
+ #
457
+ # set = Net::IMAP::SequenceSet.new(1)
458
+ # set.valid_string #=> "1"
459
+ # set = Net::IMAP::SequenceSet.new(1..100)
460
+ # set.valid_string #=> "1:100"
461
+ # set = Net::IMAP::SequenceSet.new(1...100)
462
+ # set.valid_string #=> "1:99"
463
+ # set = Net::IMAP::SequenceSet.new([1, 2, 5..])
464
+ # set.valid_string #=> "1:2,5:*"
465
+ # set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
466
+ # set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
467
+ # set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
468
+ # set.valid_string #=> "1:10,55,1024:2048"
469
+ #
470
+ # With no arguments (or +nil+) creates an empty sequence set. Note that
471
+ # an empty sequence set is invalid in the \IMAP grammar.
472
+ #
473
+ # set = Net::IMAP::SequenceSet.new
474
+ # set.empty? #=> true
475
+ # set.valid? #=> false
476
+ # set.valid_string #!> raises DataFormatError
477
+ # set << 1..10
478
+ # set.empty? #=> false
479
+ # set.valid? #=> true
480
+ # set.valid_string #=> "1:10"
481
+ #
482
+ # When +input+ is a SequenceSet, ::new behaves the same as calling #dup on
483
+ # that other set. The input's #string will be preserved.
484
+ #
485
+ # input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
486
+ # copy = Net::IMAP::SequenceSet.new(input)
487
+ # input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
488
+ # copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
489
+ # copy2 = input.dup # same as calling new with a SequenceSet input
490
+ # copy == input #=> true, same set membership
491
+ # copy.eql? input #=> true, same string value
492
+ # copy.equal? input #=> false, different objects
493
+ #
494
+ # copy.normalize!
495
+ # copy.valid_string #=> "1:10,1024,2048"
496
+ # copy == input #=> true, same set membership
497
+ # copy.eql? input #=> false, different string value
498
+ #
499
+ # copy << 999
500
+ # copy.valid_string #=> "1:10,999,1024,2048"
501
+ # copy == input #=> false, different set membership
502
+ # copy.eql? input #=> false, different string value
503
+ #
504
+ # === Alternative set creation methods
505
+ #
506
+ # * ::[] returns a frozen validated (non-empty) SequenceSet, without
507
+ # allocating a new object when the input is already a valid frozen
508
+ # SequenceSet.
509
+ # * Net::IMAP::SequenceSet() coerces an input to SequenceSet, without
510
+ # allocating a new object when the input is already a SequenceSet.
511
+ # * ::try_convert calls +to_sequence_set+ on inputs that support it and
512
+ # returns +nil+ for inputs that don't.
513
+ # * ::empty and ::full both return frozen singleton sets which can be
514
+ # combined with set operations (#|, #&, #^, #-, etc) to make new sets.
515
+ #
516
+ # See SequenceSet@Creating+sequence+sets.
371
517
  def initialize(input = nil) input ? replace(input) : clear end
372
518
 
373
519
  # Removes all elements and returns self.
374
- def clear; @tuples, @string = [], nil; self end
520
+ def clear
521
+ modifying! # redundant check, to normalize the error message for JRuby
522
+ @tuples, @string = [], nil
523
+ self
524
+ end
375
525
 
376
526
  # Replace the contents of the set with the contents of +other+ and returns
377
527
  # +self+.
378
528
  #
379
- # +other+ may be another SequenceSet, or it may be an IMAP +sequence-set+
380
- # string, a number, a range, <tt>*</tt>, or an enumerable of these.
529
+ # +other+ may be another SequenceSet or any other object that would be
530
+ # accepted by ::new.
381
531
  def replace(other)
382
532
  case other
383
533
  when SequenceSet then initialize_dup(other)
@@ -427,11 +577,13 @@ module Net
427
577
  if str.nil?
428
578
  clear
429
579
  else
580
+ modifying! # redundant check, to normalize the error message for JRuby
430
581
  str = String.try_convert(str) or raise ArgumentError, "not a string"
431
582
  tuples = str_to_tuples str
432
583
  @tuples, @string = [], -str
433
584
  tuples_add tuples
434
585
  end
586
+ str
435
587
  end
436
588
 
437
589
  # Returns the \IMAP +sequence-set+ string representation, or an empty
@@ -491,8 +643,9 @@ module Net
491
643
 
492
644
  # :call-seq: self === other -> true | false | nil
493
645
  #
494
- # Returns whether +other+ is contained within the set. Returns +nil+ if a
495
- # StandardError is raised while converting +other+ to a comparable type.
646
+ # Returns whether +other+ is contained within the set. +other+ may be any
647
+ # object that would be accepted by ::new. Returns +nil+ if StandardError
648
+ # is raised while converting +other+ to a comparable type.
496
649
  #
497
650
  # Related: #cover?, #include?, #include_star?
498
651
  def ===(other)
@@ -506,12 +659,12 @@ module Net
506
659
  # Returns whether +other+ is contained within the set. +other+ may be any
507
660
  # object that would be accepted by ::new.
508
661
  #
509
- # Related: #===, #include?, #include_star?
662
+ # Related: #===, #include?, #include_star?, #intersect?
510
663
  def cover?(other) input_to_tuples(other).none? { !include_tuple?(_1) } end
511
664
 
512
665
  # Returns +true+ when a given number or range is in +self+, and +false+
513
- # otherwise. Returns +false+ unless +number+ is an Integer, Range, or
514
- # <tt>*</tt>.
666
+ # otherwise. Returns +nil+ when +number+ isn't a valid SequenceSet
667
+ # element (Integer, Range, <tt>*</tt>, +sequence-set+ string).
515
668
  #
516
669
  # set = Net::IMAP::SequenceSet["5:10,100,111:115"]
517
670
  # set.include? 1 #=> false
@@ -519,8 +672,8 @@ module Net
519
672
  # set.include? 11..20 #=> false
520
673
  # set.include? 100 #=> true
521
674
  # set.include? 6 #=> true, covered by "5:10"
522
- # set.include? 4..9 #=> true, covered by "5:10"
523
- # set.include? "4:9" #=> true, strings are parsed
675
+ # set.include? 6..9 #=> true, covered by "5:10"
676
+ # set.include? "6:9" #=> true, strings are parsed
524
677
  # set.include? 4..9 #=> false, intersection is not sufficient
525
678
  # set.include? "*" #=> false, use #limit to re-interpret "*"
526
679
  # set.include? -1 #=> false, -1 is interpreted as "*"
@@ -529,11 +682,14 @@ module Net
529
682
  # set.include? :* #=> true
530
683
  # set.include? "*" #=> true
531
684
  # set.include? -1 #=> true
532
- # set.include? 200.. #=> true
533
- # set.include? 100.. #=> false
685
+ # set.include?(200..) #=> true
686
+ # set.include?(100..) #=> false
534
687
  #
535
- # Related: #include_star?, #cover?, #===
536
- def include?(element) include_tuple? input_to_tuple element end
688
+ # Related: #include_star?, #cover?, #===, #intersect?
689
+ def include?(element)
690
+ tuple = input_to_tuple element rescue nil
691
+ !!include_tuple?(tuple) if tuple
692
+ end
537
693
 
538
694
  alias member? include?
539
695
 
@@ -546,7 +702,7 @@ module Net
546
702
  # Net::IMAP::SequenceSet["5:10"].intersect? "7,9,11" #=> true
547
703
  # Net::IMAP::SequenceSet["5:10"].intersect? "11:33" #=> false
548
704
  #
549
- # Related: #intersection, #disjoint?
705
+ # Related: #intersection, #disjoint?, #cover?, #include?
550
706
  def intersect?(other)
551
707
  valid? && input_to_tuples(other).any? { intersect_tuple? _1 }
552
708
  end
@@ -563,26 +719,53 @@ module Net
563
719
  empty? || input_to_tuples(other).none? { intersect_tuple? _1 }
564
720
  end
565
721
 
566
- # :call-seq: max(star: :*) => integer or star or nil
722
+ # :call-seq:
723
+ # max(star: :*) => integer or star or nil
724
+ # max(count) => SequenceSet
567
725
  #
568
726
  # Returns the maximum value in +self+, +star+ when the set includes
569
727
  # <tt>*</tt>, or +nil+ when the set is empty.
570
- def max(star: :*)
571
- (val = @tuples.last&.last) && val == STAR_INT ? star : val
728
+ #
729
+ # When +count+ is given, a new SequenceSet is returned, containing only
730
+ # the last +count+ numbers. An empty SequenceSet is returned when +self+
731
+ # is empty. (+star+ is ignored when +count+ is given.)
732
+ #
733
+ # Related: #min, #minmax, #slice
734
+ def max(count = nil, star: :*)
735
+ if count
736
+ slice(-[count, size].min..) || remain_frozen_empty
737
+ elsif (val = @tuples.last&.last)
738
+ val == STAR_INT ? star : val
739
+ end
572
740
  end
573
741
 
574
- # :call-seq: min(star: :*) => integer or star or nil
742
+ # :call-seq:
743
+ # min(star: :*) => integer or star or nil
744
+ # min(count) => SequenceSet
575
745
  #
576
746
  # Returns the minimum value in +self+, +star+ when the only value in the
577
747
  # set is <tt>*</tt>, or +nil+ when the set is empty.
578
- def min(star: :*)
579
- (val = @tuples.first&.first) && val == STAR_INT ? star : val
748
+ #
749
+ # When +count+ is given, a new SequenceSet is returned, containing only
750
+ # the first +count+ numbers. An empty SequenceSet is returned when +self+
751
+ # is empty. (+star+ is ignored when +count+ is given.)
752
+ #
753
+ # Related: #max, #minmax, #slice
754
+ def min(count = nil, star: :*)
755
+ if count
756
+ slice(0...count) || remain_frozen_empty
757
+ elsif (val = @tuples.first&.first)
758
+ val != STAR_INT ? val : star
759
+ end
580
760
  end
581
761
 
582
- # :call-seq: minmax(star: :*) => nil or [integer, integer or star]
762
+ # :call-seq: minmax(star: :*) => [min, max] or nil
583
763
  #
584
764
  # Returns a 2-element array containing the minimum and maximum numbers in
585
- # +self+, or +nil+ when the set is empty.
765
+ # +self+, or +nil+ when the set is empty. +star+ is handled the same way
766
+ # as by #min and #max.
767
+ #
768
+ # Related: #min, #max
586
769
  def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end
587
770
 
588
771
  # Returns false when the set is empty.
@@ -602,14 +785,19 @@ module Net
602
785
  # Returns a new sequence set that has every number in the +other+ object
603
786
  # added.
604
787
  #
605
- # +other+ may be any object that would be accepted by ::new: a non-zero 32
606
- # bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
607
- # another sequence set, or an enumerable containing any of these.
788
+ # +other+ may be any object that would be accepted by ::new.
608
789
  #
609
790
  # Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
610
791
  # #=> Net::IMAP::SequenceSet["1:6,99"]
611
792
  #
612
- # Related: #add, #merge
793
+ # Related: #add, #merge, #&, #-, #^, #~
794
+ #
795
+ # ==== Set identities
796
+ #
797
+ # <tt>lhs | rhs</tt> is equivalent to:
798
+ # * <tt>rhs | lhs</tt> (commutative)
799
+ # * <tt>~(~lhs & ~rhs)</tt> (De Morgan's Law)
800
+ # * <tt>(lhs & rhs) ^ (lhs ^ rhs)</tt>
613
801
  def |(other) remain_frozen dup.merge other end
614
802
  alias :+ :|
615
803
  alias union :|
@@ -621,14 +809,22 @@ module Net
621
809
  # Returns a new sequence set built by duplicating this set and removing
622
810
  # every number that appears in +other+.
623
811
  #
624
- # +other+ may be any object that would be accepted by ::new: a non-zero 32
625
- # bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
626
- # another sequence set, or an enumerable containing any of these.
812
+ # +other+ may be any object that would be accepted by ::new.
627
813
  #
628
814
  # Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
629
815
  # #=> Net::IMAP::SequenceSet["1,3,5"]
630
816
  #
631
- # Related: #subtract
817
+ # Related: #subtract, #|, #&, #^, #~
818
+ #
819
+ # ==== Set identities
820
+ #
821
+ # <tt>lhs - rhs</tt> is equivalent to:
822
+ # * <tt>~rhs - ~lhs</tt>
823
+ # * <tt>lhs & ~rhs</tt>
824
+ # * <tt>~(~lhs | rhs)</tt>
825
+ # * <tt>lhs & (lhs ^ rhs)</tt>
826
+ # * <tt>lhs ^ (lhs & rhs)</tt>
827
+ # * <tt>rhs ^ (lhs | rhs)</tt>
632
828
  def -(other) remain_frozen dup.subtract other end
633
829
  alias difference :-
634
830
 
@@ -639,14 +835,22 @@ module Net
639
835
  # Returns a new sequence set containing only the numbers common to this
640
836
  # set and +other+.
641
837
  #
642
- # +other+ may be any object that would be accepted by ::new: a non-zero 32
643
- # bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
644
- # another sequence set, or an enumerable containing any of these.
838
+ # +other+ may be any object that would be accepted by ::new.
645
839
  #
646
840
  # Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
647
841
  # #=> Net::IMAP::SequenceSet["2,4"]
648
842
  #
649
- # <tt>(seqset & other)</tt> is equivalent to <tt>(seqset - ~other)</tt>.
843
+ # Related: #intersect?, #|, #-, #^, #~
844
+ #
845
+ # ==== Set identities
846
+ #
847
+ # <tt>lhs & rhs</tt> is equivalent to:
848
+ # * <tt>rhs & lhs</tt> (commutative)
849
+ # * <tt>~(~lhs | ~rhs)</tt> (De Morgan's Law)
850
+ # * <tt>lhs - ~rhs</tt>
851
+ # * <tt>lhs - (lhs - rhs)</tt>
852
+ # * <tt>lhs - (lhs ^ rhs)</tt>
853
+ # * <tt>lhs ^ (lhs - rhs)</tt>
650
854
  def &(other)
651
855
  remain_frozen dup.subtract SequenceSet.new(other).complement!
652
856
  end
@@ -659,16 +863,22 @@ module Net
659
863
  # Returns a new sequence set containing numbers that are exclusive between
660
864
  # this set and +other+.
661
865
  #
662
- # +other+ may be any object that would be accepted by ::new: a non-zero 32
663
- # bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
664
- # another sequence set, or an enumerable containing any of these.
866
+ # +other+ may be any object that would be accepted by ::new.
665
867
  #
666
868
  # Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
667
869
  # #=> Net::IMAP::SequenceSet["1,3,5:6"]
668
870
  #
669
- # <tt>(seqset ^ other)</tt> is equivalent to <tt>((seqset | other) -
670
- # (seqset & other))</tt>.
671
- def ^(other) remain_frozen (self | other).subtract(self & other) end
871
+ # Related: #|, #&, #-, #~
872
+ #
873
+ # ==== Set identities
874
+ #
875
+ # <tt>lhs ^ rhs</tt> is equivalent to:
876
+ # * <tt>rhs ^ lhs</tt> (commutative)
877
+ # * <tt>~lhs ^ ~rhs</tt>
878
+ # * <tt>(lhs | rhs) - (lhs & rhs)</tt>
879
+ # * <tt>(lhs - rhs) | (rhs - lhs)</tt>
880
+ # * <tt>(lhs ^ other) ^ (other ^ rhs)</tt>
881
+ def ^(other) remain_frozen (dup | other).subtract(self & other) end
672
882
  alias xor :^
673
883
 
674
884
  # :call-seq:
@@ -685,21 +895,30 @@ module Net
685
895
  # ~Net::IMAP::SequenceSet["6:99,223:*"]
686
896
  # #=> Net::IMAP::SequenceSet["1:5,100:222"]
687
897
  #
688
- # Related: #complement!
898
+ # Related: #complement!, #|, #&, #-, #^
899
+ #
900
+ # ==== Set identities
901
+ #
902
+ # <tt>~set</tt> is equivalent to:
903
+ # * <tt>full - set</tt>, where "full" is Net::IMAP::SequenceSet.full
689
904
  def ~; remain_frozen dup.complement! end
690
905
  alias complement :~
691
906
 
692
907
  # :call-seq:
693
- # add(object) -> self
908
+ # add(element) -> self
694
909
  # self << other -> self
695
910
  #
696
911
  # Adds a range or number to the set and returns +self+.
697
912
  #
698
913
  # #string will be regenerated. Use #merge to add many elements at once.
699
914
  #
700
- # Related: #add?, #merge, #union
701
- def add(object)
702
- tuple_add input_to_tuple object
915
+ # Use #append to append new elements to #string. See
916
+ # SequenceSet@Ordered+and+Normalized+sets.
917
+ #
918
+ # Related: #add?, #merge, #union, #append
919
+ def add(element)
920
+ modifying! # short-circuit before input_to_tuple
921
+ tuple_add input_to_tuple element
703
922
  normalize!
704
923
  end
705
924
  alias << add
@@ -708,9 +927,13 @@ module Net
708
927
  #
709
928
  # Unlike #add, #merge, or #union, the new value is appended to #string.
710
929
  # This may result in a #string which has duplicates or is out-of-order.
711
- def append(object)
712
- modifying!
713
- tuple = input_to_tuple object
930
+ #
931
+ # See SequenceSet@Ordered+and+Normalized+sets.
932
+ #
933
+ # Related: #add, #merge, #union
934
+ def append(entry)
935
+ modifying! # short-circuit before input_to_tuple
936
+ tuple = input_to_tuple entry
714
937
  entry = tuple_to_str tuple
715
938
  string unless empty? # write @string before tuple_add
716
939
  tuple_add tuple
@@ -718,19 +941,20 @@ module Net
718
941
  self
719
942
  end
720
943
 
721
- # :call-seq: add?(object) -> self or nil
944
+ # :call-seq: add?(element) -> self or nil
722
945
  #
723
946
  # Adds a range or number to the set and returns +self+. Returns +nil+
724
- # when the object is already included in the set.
947
+ # when the element is already included in the set.
725
948
  #
726
949
  # #string will be regenerated. Use #merge to add many elements at once.
727
950
  #
728
951
  # Related: #add, #merge, #union, #include?
729
- def add?(object)
730
- add object unless include? object
952
+ def add?(element)
953
+ modifying! # short-circuit before include?
954
+ add element unless include? element
731
955
  end
732
956
 
733
- # :call-seq: delete(object) -> self
957
+ # :call-seq: delete(element) -> self
734
958
  #
735
959
  # Deletes the given range or number from the set and returns +self+.
736
960
  #
@@ -738,8 +962,9 @@ module Net
738
962
  # many elements at once.
739
963
  #
740
964
  # Related: #delete?, #delete_at, #subtract, #difference
741
- def delete(object)
742
- tuple_subtract input_to_tuple object
965
+ def delete(element)
966
+ modifying! # short-circuit before input_to_tuple
967
+ tuple_subtract input_to_tuple element
743
968
  normalize!
744
969
  end
745
970
 
@@ -775,8 +1000,9 @@ module Net
775
1000
  # #string will be regenerated after deletion.
776
1001
  #
777
1002
  # Related: #delete, #delete_at, #subtract, #difference, #disjoint?
778
- def delete?(object)
779
- tuple = input_to_tuple object
1003
+ def delete?(element)
1004
+ modifying! # short-circuit before input_to_tuple
1005
+ tuple = input_to_tuple element
780
1006
  if tuple.first == tuple.last
781
1007
  return unless include_tuple? tuple
782
1008
  tuple_subtract tuple
@@ -816,37 +1042,33 @@ module Net
816
1042
  #
817
1043
  # Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference
818
1044
  def slice!(index, length = nil)
1045
+ modifying! # short-circuit before slice
819
1046
  deleted = slice(index, length) and subtract deleted
820
1047
  deleted
821
1048
  end
822
1049
 
823
- # Merges all of the elements that appear in any of the +inputs+ into the
1050
+ # Merges all of the elements that appear in any of the +sets+ into the
824
1051
  # set, and returns +self+.
825
1052
  #
826
- # The +inputs+ may be any objects that would be accepted by ::new:
827
- # non-zero 32 bit unsigned integers, ranges, <tt>sequence-set</tt>
828
- # formatted strings, other sequence sets, or enumerables containing any of
829
- # these.
1053
+ # The +sets+ may be any objects that would be accepted by ::new.
830
1054
  #
831
- # #string will be regenerated after all inputs have been merged.
1055
+ # #string will be regenerated after all sets have been merged.
832
1056
  #
833
1057
  # Related: #add, #add?, #union
834
- def merge(*inputs)
835
- tuples_add input_to_tuples inputs
1058
+ def merge(*sets)
1059
+ modifying! # short-circuit before input_to_tuples
1060
+ tuples_add input_to_tuples sets
836
1061
  normalize!
837
1062
  end
838
1063
 
839
- # Removes all of the elements that appear in any of the given +objects+
840
- # from the set, and returns +self+.
1064
+ # Removes all of the elements that appear in any of the given +sets+ from
1065
+ # the set, and returns +self+.
841
1066
  #
842
- # The +objects+ may be any objects that would be accepted by ::new:
843
- # non-zero 32 bit unsigned integers, ranges, <tt>sequence-set</tt>
844
- # formatted strings, other sequence sets, or enumerables containing any of
845
- # these.
1067
+ # The +sets+ may be any objects that would be accepted by ::new.
846
1068
  #
847
1069
  # Related: #difference
848
- def subtract(*objects)
849
- tuples_subtract input_to_tuples objects
1070
+ def subtract(*sets)
1071
+ tuples_subtract input_to_tuples sets
850
1072
  normalize!
851
1073
  end
852
1074
 
@@ -858,21 +1080,21 @@ module Net
858
1080
  # This is useful when the given order is significant, for example in a
859
1081
  # ESEARCH response to IMAP#sort.
860
1082
  #
1083
+ # See SequenceSet@Ordered+and+Normalized+sets.
1084
+ #
861
1085
  # Related: #each_entry, #elements
862
1086
  def entries; each_entry.to_a end
863
1087
 
864
1088
  # Returns an array of ranges and integers and <tt>:*</tt>.
865
1089
  #
866
1090
  # The returned elements are sorted and coalesced, even when the input
867
- # #string is not. <tt>*</tt> will sort last. See #normalize.
1091
+ # #string is not. <tt>*</tt> will sort last. See #normalize,
1092
+ # SequenceSet@Ordered+and+Normalized+sets.
868
1093
  #
869
1094
  # By itself, <tt>*</tt> translates to <tt>:*</tt>. A range containing
870
1095
  # <tt>*</tt> translates to an endless range. Use #limit to translate both
871
1096
  # cases to a maximum value.
872
1097
  #
873
- # The returned elements will be sorted and coalesced, even when the input
874
- # #string is not. <tt>*</tt> will sort last. See #normalize.
875
- #
876
1098
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
877
1099
  # #=> [2, 5..9, 11..12, :*]
878
1100
  #
@@ -883,15 +1105,13 @@ module Net
883
1105
  # Returns an array of ranges
884
1106
  #
885
1107
  # The returned elements are sorted and coalesced, even when the input
886
- # #string is not. <tt>*</tt> will sort last. See #normalize.
1108
+ # #string is not. <tt>*</tt> will sort last. See #normalize,
1109
+ # SequenceSet@Ordered+and+Normalized+sets.
887
1110
  #
888
1111
  # <tt>*</tt> translates to an endless range. By itself, <tt>*</tt>
889
1112
  # translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
890
1113
  # value.
891
1114
  #
892
- # The returned ranges will be sorted and coalesced, even when the input
893
- # #string is not. <tt>*</tt> will sort last. See #normalize.
894
- #
895
1115
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
896
1116
  # #=> [2..2, 5..9, 11..12, :*..]
897
1117
  # Net::IMAP::SequenceSet["123,999:*,456:789"].ranges
@@ -903,7 +1123,7 @@ module Net
903
1123
  # Returns a sorted array of all of the number values in the sequence set.
904
1124
  #
905
1125
  # The returned numbers are sorted and de-duplicated, even when the input
906
- # #string is not. See #normalize.
1126
+ # #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
907
1127
  #
908
1128
  # Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
909
1129
  # #=> [2, 5, 6, 7, 8, 9, 11, 12]
@@ -935,6 +1155,8 @@ module Net
935
1155
  # no sorting, deduplication, or coalescing. When #string is in its
936
1156
  # normalized form, this will yield the same values as #each_element.
937
1157
  #
1158
+ # See SequenceSet@Ordered+and+Normalized+sets.
1159
+ #
938
1160
  # Related: #entries, #each_element
939
1161
  def each_entry(&block) # :yields: integer or range or :*
940
1162
  return to_enum(__method__) unless block_given?
@@ -945,7 +1167,7 @@ module Net
945
1167
  # and returns self. Returns an enumerator when called without a block.
946
1168
  #
947
1169
  # The returned numbers are sorted and de-duplicated, even when the input
948
- # #string is not. See #normalize.
1170
+ # #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
949
1171
  #
950
1172
  # Related: #elements, #each_entry
951
1173
  def each_element # :yields: integer or range or :*
@@ -1239,20 +1461,76 @@ module Net
1239
1461
  def slice_range(range)
1240
1462
  first = range.begin || 0
1241
1463
  last = range.end || -1
1242
- last -= 1 if range.exclude_end? && range.end && last != STAR_INT
1464
+ if range.exclude_end?
1465
+ return remain_frozen_empty if last.zero?
1466
+ last -= 1 if range.end && last != STAR_INT
1467
+ end
1243
1468
  if (first * last).positive? && last < first
1244
- SequenceSet.empty
1469
+ remain_frozen_empty
1245
1470
  elsif (min = at(first))
1246
1471
  max = at(last)
1472
+ max = :* if max.nil?
1247
1473
  if max == :* then self & (min..)
1248
1474
  elsif min <= max then self & (min..max)
1249
- else SequenceSet.empty
1475
+ else remain_frozen_empty
1250
1476
  end
1251
1477
  end
1252
1478
  end
1253
1479
 
1254
1480
  public
1255
1481
 
1482
+ # Returns a copy of +self+ which only contains the numbers above +num+.
1483
+ #
1484
+ # Net::IMAP::SequenceSet["5,10:22,50"].above(10) # to_s => "11:22,50"
1485
+ # Net::IMAP::SequenceSet["5,10:22,50"].above(20) # to_s => "21:22,50
1486
+ # Net::IMAP::SequenceSet["5,10:22,50"].above(30) # to_s => "50"
1487
+ #
1488
+ # This returns the same result as #intersection with <tt>((num+1)..)</tt>
1489
+ # or #difference with <tt>(..num)</tt>.
1490
+ #
1491
+ # Net::IMAP::SequenceSet["5,10:22,50"] & (11..) # to_s => "11:22,50"
1492
+ # Net::IMAP::SequenceSet["5,10:22,50"] - (..10) # to_s => "11:22,50"
1493
+ # Net::IMAP::SequenceSet["5,10:22,50"] & (21..) # to_s => "21:22,50"
1494
+ # Net::IMAP::SequenceSet["5,10:22,50"] - (..20) # to_s => "21:22,50"
1495
+ #
1496
+ # Related: #above, #-, #&
1497
+ def above(num)
1498
+ NumValidator.valid_nz_number?(num) or
1499
+ raise ArgumentError, "not a valid sequence set number"
1500
+ difference(..num)
1501
+ end
1502
+
1503
+ # Returns a copy of +self+ which only contains numbers below +num+.
1504
+ #
1505
+ # Net::IMAP::SequenceSet["5,10:22,50"].below(10) # to_s => "5"
1506
+ # Net::IMAP::SequenceSet["5,10:22,50"].below(20) # to_s => "5,10:19"
1507
+ # Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
1508
+ #
1509
+ # This returns the same result as #intersection with <tt>(..(num-1))</tt>
1510
+ # or #difference with <tt>(num..)</tt>.
1511
+ #
1512
+ # Net::IMAP::SequenceSet["5,10:22,50"] & (..9) # to_s => "5"
1513
+ # Net::IMAP::SequenceSet["5,10:22,50"] - (10..) # to_s => "5"
1514
+ # Net::IMAP::SequenceSet["5,10:22,50"] & (..19) # to_s => "5,10:19"
1515
+ # Net::IMAP::SequenceSet["5,10:22,50"] - (20..) # to_s => "5,10:19"
1516
+ #
1517
+ # When the set does not contain <tt>*</tt>, #below is identical to #limit
1518
+ # with <tt>max: num - 1</tt>. When the set does contain <tt>*</tt>,
1519
+ # #below always drops it from the result. Use #limit when the IMAP
1520
+ # semantics for <tt>*</tt> must be enforced.
1521
+ #
1522
+ # Net::IMAP::SequenceSet["5,10:22,50"].below(30) # to_s => "5,10:22"
1523
+ # Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 29) # to_s => "5,10:22"
1524
+ # Net::IMAP::SequenceSet["5,10:22,*"].below(30) # to_s => "5,10:22"
1525
+ # Net::IMAP::SequenceSet["5,10:22,*"].limit(max: 29) # to_s => "5,10:22,29"
1526
+ #
1527
+ # Related: #above, #-, #&, #limit
1528
+ def below(num)
1529
+ NumValidator.valid_nz_number?(num) or
1530
+ raise ArgumentError, "not a valid sequence set number"
1531
+ difference(num..)
1532
+ end
1533
+
1256
1534
  # Returns a frozen SequenceSet with <tt>*</tt> converted to +max+, numbers
1257
1535
  # and ranges over +max+ removed, and ranges containing +max+ converted to
1258
1536
  # end at +max+.
@@ -1270,6 +1548,7 @@ module Net
1270
1548
  # Net::IMAP::SequenceSet["500:*"].limit(max: 37)
1271
1549
  # #=> Net::IMAP::SequenceSet["37"]
1272
1550
  #
1551
+ # Related: #limit!
1273
1552
  def limit(max:)
1274
1553
  max = to_tuple_int(max)
1275
1554
  if empty? then self.class.empty
@@ -1284,6 +1563,7 @@ module Net
1284
1563
  #
1285
1564
  # Related: #limit
1286
1565
  def limit!(max:)
1566
+ modifying! # short-circuit, and normalize the error message for JRuby
1287
1567
  star = include_star?
1288
1568
  max = to_tuple_int(max)
1289
1569
  tuple_subtract [max + 1, STAR_INT]
@@ -1298,6 +1578,7 @@ module Net
1298
1578
  #
1299
1579
  # Related: #complement
1300
1580
  def complement!
1581
+ modifying! # short-circuit, and normalize the error message for JRuby
1301
1582
  return replace(self.class.full) if empty?
1302
1583
  return clear if full?
1303
1584
  flat = @tuples.flat_map { [_1 - 1, _2 + 1] }
@@ -1311,6 +1592,7 @@ module Net
1311
1592
  #
1312
1593
  # The returned set's #string is sorted and deduplicated. Adjacent or
1313
1594
  # overlapping elements will be merged into a single larger range.
1595
+ # See SequenceSet@Ordered+and+Normalized+sets.
1314
1596
  #
1315
1597
  # Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
1316
1598
  # #=> Net::IMAP::SequenceSet["1:7,9:11"]
@@ -1323,21 +1605,24 @@ module Net
1323
1605
  end
1324
1606
 
1325
1607
  # Resets #string to be sorted, deduplicated, and coalesced. Returns
1326
- # +self+.
1608
+ # +self+. See SequenceSet@Ordered+and+Normalized+sets.
1327
1609
  #
1328
1610
  # Related: #normalize, #normalized_string
1329
1611
  def normalize!
1612
+ modifying! # redundant check, to normalize the error message for JRuby
1330
1613
  @string = nil
1331
1614
  self
1332
1615
  end
1333
1616
 
1334
1617
  # Returns a normalized +sequence-set+ string representation, sorted
1335
1618
  # and deduplicated. Adjacent or overlapping elements will be merged into
1336
- # a single larger range. Returns +nil+ when the set is empty.
1619
+ # a single larger range. See SequenceSet@Ordered+and+Normalized+sets.
1337
1620
  #
1338
1621
  # Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
1339
1622
  # #=> "1:7,9:11"
1340
1623
  #
1624
+ # Returns +nil+ when the set is empty.
1625
+ #
1341
1626
  # Related: #normalize!, #normalize
1342
1627
  def normalized_string
1343
1628
  @tuples.empty? ? nil : -@tuples.map { tuple_to_str _1 }.join(",")
@@ -1353,7 +1638,15 @@ module Net
1353
1638
  end
1354
1639
  end
1355
1640
 
1356
- # Returns self
1641
+ ##
1642
+ # :method: to_sequence_set
1643
+ # :call-seq: to_sequence_set -> self
1644
+ #
1645
+ # Returns +self+
1646
+ #
1647
+ # Related: ::try_convert
1648
+
1649
+ # :nodoc: (work around rdoc bug)
1357
1650
  alias to_sequence_set itself
1358
1651
 
1359
1652
  # Unstable API: currently for internal use only (Net::IMAP#validate_data)
@@ -1367,6 +1660,18 @@ module Net
1367
1660
  imap.__send__(:put_string, valid_string)
1368
1661
  end
1369
1662
 
1663
+ # For YAML serialization
1664
+ def encode_with(coder) # :nodoc:
1665
+ # we can perfectly reconstruct from the string
1666
+ coder['string'] = to_s
1667
+ end
1668
+
1669
+ # For YAML deserialization
1670
+ def init_with(coder) # :nodoc:
1671
+ @tuples = []
1672
+ self.string = coder['string']
1673
+ end
1674
+
1370
1675
  protected
1371
1676
 
1372
1677
  attr_reader :tuples # :nodoc:
@@ -1374,6 +1679,7 @@ module Net
1374
1679
  private
1375
1680
 
1376
1681
  def remain_frozen(set) frozen? ? set.freeze : set end
1682
+ def remain_frozen_empty; frozen? ? SequenceSet.empty : SequenceSet.new end
1377
1683
 
1378
1684
  # frozen clones are shallow copied
1379
1685
  def initialize_clone(other)
@@ -1381,35 +1687,35 @@ module Net
1381
1687
  end
1382
1688
 
1383
1689
  def initialize_dup(other)
1690
+ modifying! # redundant check, to normalize the error message for JRuby
1384
1691
  @tuples = other.tuples.map(&:dup)
1385
1692
  @string = other.string&.-@
1386
1693
  super
1387
1694
  end
1388
1695
 
1389
- def input_to_tuple(obj)
1390
- obj = input_try_convert obj
1391
- case obj
1392
- when *STARS, Integer then [int = to_tuple_int(obj), int]
1393
- when Range then range_to_tuple(obj)
1394
- when String then str_to_tuple(obj)
1696
+ def input_to_tuple(entry)
1697
+ entry = input_try_convert entry
1698
+ case entry
1699
+ when *STARS, Integer then [int = to_tuple_int(entry), int]
1700
+ when Range then range_to_tuple(entry)
1701
+ when String then str_to_tuple(entry)
1395
1702
  else
1396
- raise DataFormatError, "expected number or range, got %p" % [obj]
1703
+ raise DataFormatError, "expected number or range, got %p" % [entry]
1397
1704
  end
1398
1705
  end
1399
1706
 
1400
- def input_to_tuples(obj)
1401
- obj = input_try_convert obj
1402
- case obj
1403
- when *STARS, Integer, Range then [input_to_tuple(obj)]
1404
- when String then str_to_tuples obj
1405
- when SequenceSet then obj.tuples
1406
- when Set then obj.map { [to_tuple_int(_1)] * 2 }
1407
- when Array then obj.flat_map { input_to_tuples _1 }
1707
+ def input_to_tuples(set)
1708
+ set = input_try_convert set
1709
+ case set
1710
+ when *STARS, Integer, Range then [input_to_tuple(set)]
1711
+ when String then str_to_tuples set
1712
+ when SequenceSet then set.tuples
1713
+ when Set then set.map { [to_tuple_int(_1)] * 2 }
1714
+ when Array then set.flat_map { input_to_tuples _1 }
1408
1715
  when nil then []
1409
1716
  else
1410
- raise DataFormatError,
1411
- "expected nz-number, range, string, or enumerable; " \
1412
- "got %p" % [obj]
1717
+ raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
1718
+ "got %p" % [set]
1413
1719
  end
1414
1720
  end
1415
1721