net-imap 0.4.12 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +8 -1
  3. data/README.md +10 -4
  4. data/docs/styles.css +75 -14
  5. data/lib/net/imap/authenticators.rb +2 -2
  6. data/lib/net/imap/command_data.rb +61 -48
  7. data/lib/net/imap/config/attr_accessors.rb +75 -0
  8. data/lib/net/imap/config/attr_inheritance.rb +90 -0
  9. data/lib/net/imap/config/attr_type_coercion.rb +61 -0
  10. data/lib/net/imap/config.rb +470 -0
  11. data/lib/net/imap/data_encoding.rb +3 -3
  12. data/lib/net/imap/data_lite.rb +226 -0
  13. data/lib/net/imap/deprecated_client_options.rb +8 -5
  14. data/lib/net/imap/errors.rb +6 -0
  15. data/lib/net/imap/esearch_result.rb +180 -0
  16. data/lib/net/imap/fetch_data.rb +126 -47
  17. data/lib/net/imap/response_data.rb +124 -237
  18. data/lib/net/imap/response_parser/parser_utils.rb +11 -6
  19. data/lib/net/imap/response_parser.rb +187 -34
  20. data/lib/net/imap/sasl/anonymous_authenticator.rb +3 -3
  21. data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
  22. data/lib/net/imap/sasl/authenticators.rb +8 -4
  23. data/lib/net/imap/sasl/client_adapter.rb +77 -26
  24. data/lib/net/imap/sasl/cram_md5_authenticator.rb +4 -4
  25. data/lib/net/imap/sasl/digest_md5_authenticator.rb +218 -56
  26. data/lib/net/imap/sasl/external_authenticator.rb +2 -2
  27. data/lib/net/imap/sasl/gs2_header.rb +7 -7
  28. data/lib/net/imap/sasl/login_authenticator.rb +4 -3
  29. data/lib/net/imap/sasl/oauthbearer_authenticator.rb +6 -6
  30. data/lib/net/imap/sasl/plain_authenticator.rb +7 -7
  31. data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
  32. data/lib/net/imap/sasl/scram_authenticator.rb +8 -8
  33. data/lib/net/imap/sasl.rb +7 -4
  34. data/lib/net/imap/sasl_adapter.rb +0 -1
  35. data/lib/net/imap/search_result.rb +2 -2
  36. data/lib/net/imap/sequence_set.rb +221 -82
  37. data/lib/net/imap/stringprep/nameprep.rb +1 -1
  38. data/lib/net/imap/stringprep/trace.rb +4 -4
  39. data/lib/net/imap/uidplus_data.rb +244 -0
  40. data/lib/net/imap/vanished_data.rb +56 -0
  41. data/lib/net/imap.rb +1010 -320
  42. data/net-imap.gemspec +3 -3
  43. data/rakelib/rfcs.rake +2 -0
  44. data/rakelib/string_prep_tables_generator.rb +2 -0
  45. metadata +12 -10
  46. data/.github/dependabot.yml +0 -6
  47. data/.github/workflows/pages.yml +0 -46
  48. data/.github/workflows/push_gem.yml +0 -48
  49. data/.github/workflows/test.yml +0 -31
  50. data/.gitignore +0 -12
  51. data/.mailmap +0 -13
@@ -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, or an enumerable containing any of these.
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 form
59
+ # == Ordered and Normalized sets
64
60
  #
65
- # When a sequence set is created with a single String value, that #string
66
- # representation is preserved. SequenceSet's internal representation
67
- # implicitly sorts all entries, de-duplicates numbers, and coalesces
68
- # adjacent or overlapping ranges. Most enumeration methods and offset-based
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
- # In some cases the order of the string representation is significant, such
73
- # as the +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. Use
74
- # #entries or #each_entry to enumerate the set in its original order. To
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
  #
@@ -164,7 +162,7 @@ module Net
164
162
  # - #===:
165
163
  # Returns whether a given object is fully contained within +self+, or
166
164
  # +nil+ if the object cannot be converted to a compatible type.
167
- # - #cover? (aliased as #===):
165
+ # - #cover?:
168
166
  # Returns whether a given object is fully contained within +self+.
169
167
  # - #intersect? (aliased as #overlap?):
170
168
  # Returns whether +self+ and a given object have any common elements.
@@ -185,30 +183,41 @@ module Net
185
183
  # - #max: Returns the maximum number in the set.
186
184
  # - #minmax: Returns the minimum and maximum numbers in the set.
187
185
  #
188
- # <i>Accessing value by offset:</i>
186
+ # <i>Accessing value by offset in sorted set:</i>
189
187
  # - #[] (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
188
+ # given offset or range of offsets in the sorted set.
189
+ # - #at: Returns the number at a given offset in the sorted set.
190
+ # - #find_index: Returns the given number's offset in the sorted set.
191
+ #
192
+ # <i>Accessing value by offset in ordered entries</i>
193
+ # - #ordered_at: Returns the number at a given offset in the ordered entries.
194
+ # - #find_ordered_index: Returns the index of the given number's first
195
+ # occurrence in entries.
193
196
  #
194
197
  # <i>Set cardinality:</i>
195
198
  # - #count (aliased as #size): Returns the count of numbers in the set.
199
+ # Duplicated numbers are not counted.
196
200
  # - #empty?: Returns whether the set has no members. \IMAP syntax does not
197
201
  # allow empty sequence sets.
198
202
  # - #valid?: Returns whether the set has any members.
199
203
  # - #full?: Returns whether the set contains every possible value, including
200
204
  # <tt>*</tt>.
201
205
  #
206
+ # <i>Denormalized properties:</i>
207
+ # - #has_duplicates?: Returns whether the ordered entries repeat any
208
+ # numbers.
209
+ # - #count_duplicates: Returns the count of repeated numbers in the ordered
210
+ # entries.
211
+ # - #count_with_duplicates: Returns the count of numbers in the ordered
212
+ # entries, including any repeated numbers.
213
+ #
202
214
  # === Methods for Iterating
203
215
  #
216
+ # <i>Normalized (sorted and coalesced):</i>
204
217
  # - #each_element: Yields each number and range in the set, sorted and
205
218
  # coalesced, and returns +self+.
206
219
  # - #elements (aliased as #to_a): Returns an Array of every number and range
207
220
  # 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
221
  # - #each_range:
213
222
  # Yields each element in the set as a Range and returns +self+.
214
223
  # - #ranges: Returns an Array of every element in the set, converting
@@ -218,6 +227,14 @@ module Net
218
227
  # ranges into all of their contained numbers.
219
228
  # - #to_set: Returns a Set containing all of the #numbers in the set.
220
229
  #
230
+ # <i>Order preserving:</i>
231
+ # - #each_entry: Yields each number and range in the set, unsorted and
232
+ # without deduplicating numbers or coalescing ranges, and returns +self+.
233
+ # - #entries: Returns an Array of every number and range in the set,
234
+ # unsorted and without deduplicating numbers or coalescing ranges.
235
+ # - #each_ordered_number: Yields each number in the ordered entries and
236
+ # returns +self+.
237
+ #
221
238
  # === Methods for \Set Operations
222
239
  # These methods do not modify +self+.
223
240
  #
@@ -237,19 +254,29 @@ module Net
237
254
  # === Methods for Assigning
238
255
  # These methods add or replace elements in +self+.
239
256
  #
257
+ # <i>Normalized (sorted and coalesced):</i>
258
+ #
259
+ # These methods always update #string to be fully sorted and coalesced.
260
+ #
240
261
  # - #add (aliased as #<<): Adds a given object to the set; returns +self+.
241
262
  # - #add?: If the given object is not an element in the set, adds it and
242
263
  # returns +self+; otherwise, returns +nil+.
243
264
  # - #merge: Merges multiple elements into the set; returns +self+.
265
+ # - #complement!: Replaces the contents of the set with its own #complement.
266
+ #
267
+ # <i>Order preserving:</i>
268
+ #
269
+ # These methods _may_ cause #string to not be sorted or coalesced.
270
+ #
244
271
  # - #append: Adds a given object to the set, appending it to the existing
245
272
  # string, and returns +self+.
246
273
  # - #string=: Assigns a new #string value and replaces #elements to match.
247
274
  # - #replace: Replaces the contents of the set with the contents
248
275
  # of a given object.
249
- # - #complement!: Replaces the contents of the set with its own #complement.
250
276
  #
251
277
  # === Methods for Deleting
252
- # These methods remove elements from +self+.
278
+ # These methods remove elements from +self+, and update #string to be fully
279
+ # sorted and coalesced.
253
280
  #
254
281
  # - #clear: Removes all elements in the set; returns +self+.
255
282
  # - #delete: Removes a given object from the set; returns +self+.
@@ -286,11 +313,7 @@ module Net
286
313
 
287
314
  # valid inputs for "*"
288
315
  STARS = [:*, ?*, -1].freeze
289
- private_constant :STAR_INT, :STARS
290
-
291
- COERCIBLE = ->{ _1.respond_to? :to_sequence_set }
292
- ENUMABLE = ->{ _1.respond_to?(:each) && _1.respond_to?(:empty?) }
293
- private_constant :COERCIBLE, :ENUMABLE
316
+ private_constant :STARS
294
317
 
295
318
  class << self
296
319
 
@@ -304,7 +327,7 @@ module Net
304
327
  # Use ::new to create a mutable or empty SequenceSet.
305
328
  def [](first, *rest)
306
329
  if rest.empty?
307
- if first.is_a?(SequenceSet) && set.frozen? && set.valid?
330
+ if first.is_a?(SequenceSet) && first.frozen? && first.valid?
308
331
  first
309
332
  else
310
333
  new(first).validate.freeze
@@ -325,7 +348,7 @@ module Net
325
348
  # raised.
326
349
  def try_convert(obj)
327
350
  return obj if obj.is_a?(SequenceSet)
328
- return nil unless respond_to?(:to_sequence_set)
351
+ return nil unless obj.respond_to?(:to_sequence_set)
329
352
  obj = obj.to_sequence_set
330
353
  return obj if obj.is_a?(SequenceSet)
331
354
  raise DataFormatError, "invalid object returned from to_sequence_set"
@@ -389,6 +412,10 @@ module Net
389
412
  # Related: #valid_string, #normalized_string, #to_s
390
413
  def string; @string ||= normalized_string if valid? end
391
414
 
415
+ # Returns an array with #normalized_string when valid and an empty array
416
+ # otherwise.
417
+ def deconstruct; valid? ? [normalized_string] : [] end
418
+
392
419
  # Assigns a new string to #string and resets #elements to match. It
393
420
  # cannot be set to an empty string—assign +nil+ or use #clear instead.
394
421
  # The string is validated but not normalized.
@@ -682,10 +709,12 @@ module Net
682
709
  # Unlike #add, #merge, or #union, the new value is appended to #string.
683
710
  # This may result in a #string which has duplicates or is out-of-order.
684
711
  def append(object)
712
+ modifying!
685
713
  tuple = input_to_tuple object
686
714
  entry = tuple_to_str tuple
715
+ string unless empty? # write @string before tuple_add
687
716
  tuple_add tuple
688
- @string = -(string ? "#{@string},#{entry}" : entry)
717
+ @string = -(@string ? "#{@string},#{entry}" : entry)
689
718
  self
690
719
  end
691
720
 
@@ -841,8 +870,8 @@ module Net
841
870
  # <tt>*</tt> translates to an endless range. Use #limit to translate both
842
871
  # cases to a maximum value.
843
872
  #
844
- # If the original input was unordered or contains overlapping ranges, the
845
- # returned ranges will be ordered and coalesced.
873
+ # The returned elements will be sorted and coalesced, even when the input
874
+ # #string is not. <tt>*</tt> will sort last. See #normalize.
846
875
  #
847
876
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
848
877
  # #=> [2, 5..9, 11..12, :*]
@@ -860,7 +889,7 @@ module Net
860
889
  # translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
861
890
  # value.
862
891
  #
863
- # The returned ranges will be ordered and coalesced, even when the input
892
+ # The returned ranges will be sorted and coalesced, even when the input
864
893
  # #string is not. <tt>*</tt> will sort last. See #normalize.
865
894
  #
866
895
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
@@ -909,9 +938,7 @@ module Net
909
938
  # Related: #entries, #each_element
910
939
  def each_entry(&block) # :yields: integer or range or :*
911
940
  return to_enum(__method__) unless block_given?
912
- return each_element(&block) unless @string
913
- @string.split(",").each do yield tuple_to_entry str_to_tuple _1 end
914
- self
941
+ each_entry_tuple do yield tuple_to_entry _1 end
915
942
  end
916
943
 
917
944
  # Yields each number or range (or <tt>:*</tt>) in #elements to the block
@@ -929,6 +956,16 @@ module Net
929
956
 
930
957
  private
931
958
 
959
+ def each_entry_tuple(&block)
960
+ return to_enum(__method__) unless block_given?
961
+ if @string
962
+ @string.split(",") do block.call str_to_tuple _1 end
963
+ else
964
+ @tuples.each(&block)
965
+ end
966
+ self
967
+ end
968
+
932
969
  def tuple_to_entry((min, max))
933
970
  if min == STAR_INT then :*
934
971
  elsif max == STAR_INT then min..
@@ -960,19 +997,36 @@ module Net
960
997
  # Returns an enumerator when called without a block (even if the set
961
998
  # contains <tt>*</tt>).
962
999
  #
963
- # Related: #numbers
1000
+ # Related: #numbers, #each_ordered_number
964
1001
  def each_number(&block) # :yields: integer
965
1002
  return to_enum(__method__) unless block_given?
966
1003
  raise RangeError, '%s contains "*"' % [self.class] if include_star?
967
- each_element do |elem|
968
- case elem
969
- when Range then elem.each(&block)
970
- when Integer then block.(elem)
971
- end
972
- end
1004
+ @tuples.each do each_number_in_tuple _1, _2, &block end
973
1005
  self
974
1006
  end
975
1007
 
1008
+ # Yields each number in #entries to the block and returns self.
1009
+ # If the set contains a <tt>*</tt>, RangeError will be raised.
1010
+ #
1011
+ # Returns an enumerator when called without a block (even if the set
1012
+ # contains <tt>*</tt>).
1013
+ #
1014
+ # Related: #entries, #each_number
1015
+ def each_ordered_number(&block)
1016
+ return to_enum(__method__) unless block_given?
1017
+ raise RangeError, '%s contains "*"' % [self.class] if include_star?
1018
+ each_entry_tuple do each_number_in_tuple _1, _2, &block end
1019
+ end
1020
+
1021
+ private def each_number_in_tuple(min, max, &block)
1022
+ if min == STAR_INT then yield :*
1023
+ elsif min == max then yield min
1024
+ elsif max != STAR_INT then (min..max).each(&block)
1025
+ else
1026
+ raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
1027
+ end
1028
+ end
1029
+
976
1030
  # Returns a Set with all of the #numbers in the sequence set.
977
1031
  #
978
1032
  # If the set contains a <tt>*</tt>, RangeError will be raised.
@@ -984,8 +1038,10 @@ module Net
984
1038
 
985
1039
  # Returns the count of #numbers in the set.
986
1040
  #
987
- # If <tt>*</tt> and <tt>2**32 - 1</tt> (the maximum 32-bit unsigned
988
- # integer value) are both in the set, they will only be counted once.
1041
+ # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1042
+ # unsigned integer value).
1043
+ #
1044
+ # Related: #count_with_duplicates
989
1045
  def count
990
1046
  @tuples.sum(@tuples.count) { _2 - _1 } +
991
1047
  (include_star? && include?(UINT32_MAX) ? -1 : 0)
@@ -993,33 +1049,87 @@ module Net
993
1049
 
994
1050
  alias size count
995
1051
 
996
- # Returns the index of +number+ in the set, or +nil+ if +number+ isn't in
997
- # the set.
1052
+ # Returns the count of numbers in the ordered #entries, including any
1053
+ # repeated numbers.
1054
+ #
1055
+ # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1056
+ # unsigned integer value).
1057
+ #
1058
+ # When #string is normalized, this behaves the same as #count.
1059
+ #
1060
+ # Related: #entries, #count_duplicates, #has_duplicates?
1061
+ def count_with_duplicates
1062
+ return count unless @string
1063
+ each_entry_tuple.sum {|min, max|
1064
+ max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
1065
+ }
1066
+ end
1067
+
1068
+ # Returns the count of repeated numbers in the ordered #entries, the
1069
+ # difference between #count_with_duplicates and #count.
1070
+ #
1071
+ # When #string is normalized, this is zero.
1072
+ #
1073
+ # Related: #entries, #count_with_duplicates, #has_duplicates?
1074
+ def count_duplicates
1075
+ return 0 unless @string
1076
+ count_with_duplicates - count
1077
+ end
1078
+
1079
+ # :call-seq: has_duplicates? -> true | false
1080
+ #
1081
+ # Returns whether or not the ordered #entries repeat any numbers.
1082
+ #
1083
+ # Always returns +false+ when #string is normalized.
1084
+ #
1085
+ # Related: #entries, #count_with_duplicates, #count_duplicates?
1086
+ def has_duplicates?
1087
+ return false unless @string
1088
+ count_with_duplicates != count
1089
+ end
1090
+
1091
+ # Returns the (sorted and deduplicated) index of +number+ in the set, or
1092
+ # +nil+ if +number+ isn't in the set.
998
1093
  #
999
- # Related: #[]
1094
+ # Related: #[], #at, #find_ordered_index
1000
1095
  def find_index(number)
1001
1096
  number = to_tuple_int number
1002
- each_tuple_with_index do |min, max, idx_min|
1097
+ each_tuple_with_index(@tuples) do |min, max, idx_min|
1003
1098
  number < min and return nil
1004
1099
  number <= max and return from_tuple_int(idx_min + (number - min))
1005
1100
  end
1006
1101
  nil
1007
1102
  end
1008
1103
 
1104
+ # Returns the first index of +number+ in the ordered #entries, or
1105
+ # +nil+ if +number+ isn't in the set.
1106
+ #
1107
+ # Related: #find_index
1108
+ def find_ordered_index(number)
1109
+ number = to_tuple_int number
1110
+ each_tuple_with_index(each_entry_tuple) do |min, max, idx_min|
1111
+ if min <= number && number <= max
1112
+ return from_tuple_int(idx_min + (number - min))
1113
+ end
1114
+ end
1115
+ nil
1116
+ end
1117
+
1009
1118
  private
1010
1119
 
1011
- def each_tuple_with_index
1120
+ def each_tuple_with_index(tuples)
1012
1121
  idx_min = 0
1013
- @tuples.each do |min, max|
1014
- yield min, max, idx_min, (idx_max = idx_min + (max - min))
1122
+ tuples.each do |min, max|
1123
+ idx_max = idx_min + (max - min)
1124
+ yield min, max, idx_min, idx_max
1015
1125
  idx_min = idx_max + 1
1016
1126
  end
1017
1127
  idx_min
1018
1128
  end
1019
1129
 
1020
- def reverse_each_tuple_with_index
1130
+ def reverse_each_tuple_with_index(tuples)
1021
1131
  idx_max = -1
1022
- @tuples.reverse_each do |min, max|
1132
+ tuples.reverse_each do |min, max|
1023
1133
  yield min, max, (idx_min = idx_max - (max - min)), idx_max
1024
1134
  idx_max = idx_min - 1
1025
1135
  end
@@ -1030,18 +1140,38 @@ module Net
1030
1140
 
1031
1141
  # :call-seq: at(index) -> integer or nil
1032
1142
  #
1033
- # Returns a number from +self+, without modifying the set. Behaves the
1034
- # same as #[], except that #at only allows a single integer argument.
1143
+ # Returns the number at the given +index+ in the sorted set, without
1144
+ # modifying the set.
1145
+ #
1146
+ # +index+ is interpreted the same as in #[], except that #at only allows a
1147
+ # single integer argument.
1035
1148
  #
1036
- # Related: #[], #slice
1149
+ # Related: #[], #slice, #ordered_at
1037
1150
  def at(index)
1151
+ lookup_number_by_tuple_index(tuples, index)
1152
+ end
1153
+
1154
+ # :call-seq: ordered_at(index) -> integer or nil
1155
+ #
1156
+ # Returns the number at the given +index+ in the ordered #entries, without
1157
+ # modifying the set.
1158
+ #
1159
+ # +index+ is interpreted the same as in #at (and #[]), except that
1160
+ # #ordered_at applies to the ordered #entries, not the sorted set.
1161
+ #
1162
+ # Related: #[], #slice, #ordered_at
1163
+ def ordered_at(index)
1164
+ lookup_number_by_tuple_index(each_entry_tuple, index)
1165
+ end
1166
+
1167
+ private def lookup_number_by_tuple_index(tuples, index)
1038
1168
  index = Integer(index.to_int)
1039
1169
  if index.negative?
1040
- reverse_each_tuple_with_index do |min, max, idx_min, idx_max|
1170
+ reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
1041
1171
  idx_min <= index and return from_tuple_int(min + (index - idx_min))
1042
1172
  end
1043
1173
  else
1044
- each_tuple_with_index do |min, _, idx_min, idx_max|
1174
+ each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
1045
1175
  index <= idx_max and return from_tuple_int(min + (index - idx_min))
1046
1176
  end
1047
1177
  end
@@ -1056,17 +1186,18 @@ module Net
1056
1186
  # seqset[range] -> sequence set or nil
1057
1187
  # slice(range) -> sequence set or nil
1058
1188
  #
1059
- # Returns a number or a subset from +self+, without modifying the set.
1189
+ # Returns a number or a subset from the _sorted_ set, without modifying
1190
+ # the set.
1060
1191
  #
1061
1192
  # When an Integer argument +index+ is given, the number at offset +index+
1062
- # is returned:
1193
+ # in the sorted set is returned:
1063
1194
  #
1064
1195
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1065
1196
  # set[0] #=> 10
1066
1197
  # set[5] #=> 15
1067
1198
  # set[10] #=> 26
1068
1199
  #
1069
- # If +index+ is negative, it counts relative to the end of +self+:
1200
+ # If +index+ is negative, it counts relative to the end of the sorted set:
1070
1201
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1071
1202
  # set[-1] #=> 26
1072
1203
  # set[-3] #=> 22
@@ -1078,13 +1209,14 @@ module Net
1078
1209
  # set[11] #=> nil
1079
1210
  # set[-12] #=> nil
1080
1211
  #
1081
- # The result is based on the normalized set—sorted and de-duplicatednot
1082
- # on the assigned value of #string.
1212
+ # The result is based on the sorted and de-duplicated set, not on the
1213
+ # ordered #entries in #string.
1083
1214
  #
1084
1215
  # set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
1085
1216
  # set[0] #=> 11
1086
1217
  # set[-1] #=> 23
1087
1218
  #
1219
+ # Related: #at
1088
1220
  def [](index, length = nil)
1089
1221
  if length then slice_length(index, length)
1090
1222
  elsif index.is_a?(Range) then slice_range(index)
@@ -1271,7 +1403,8 @@ module Net
1271
1403
  when *STARS, Integer, Range then [input_to_tuple(obj)]
1272
1404
  when String then str_to_tuples obj
1273
1405
  when SequenceSet then obj.tuples
1274
- when ENUMABLE then obj.flat_map { input_to_tuples _1 }
1406
+ when Set then obj.map { [to_tuple_int(_1)] * 2 }
1407
+ when Array then obj.flat_map { input_to_tuples _1 }
1275
1408
  when nil then []
1276
1409
  else
1277
1410
  raise DataFormatError,
@@ -1284,8 +1417,7 @@ module Net
1284
1417
  # String, Set, Array, or... any type of object.
1285
1418
  def input_try_convert(input)
1286
1419
  SequenceSet.try_convert(input) ||
1287
- # Integer.try_convert(input) || # ruby 3.1+
1288
- input.respond_to?(:to_int) && Integer(input.to_int) ||
1420
+ Integer.try_convert(input) ||
1289
1421
  String.try_convert(input) ||
1290
1422
  input
1291
1423
  end
@@ -1317,6 +1449,12 @@ module Net
1317
1449
  range.include?(min) || range.include?(max) || (min..max).cover?(range)
1318
1450
  end
1319
1451
 
1452
+ def modifying!
1453
+ if frozen?
1454
+ raise FrozenError, "can't modify frozen #{self.class}: %p" % [self]
1455
+ end
1456
+ end
1457
+
1320
1458
  def tuples_add(tuples) tuples.each do tuple_add _1 end; self end
1321
1459
  def tuples_subtract(tuples) tuples.each do tuple_subtract _1 end; self end
1322
1460
 
@@ -1331,10 +1469,11 @@ module Net
1331
1469
  # ---------??===lower==|--|==|----|===upper===|-- join until upper
1332
1470
  # ---------??===lower==|--|==|--|=====upper===|-- join to upper
1333
1471
  def tuple_add(tuple)
1472
+ modifying!
1334
1473
  min, max = tuple
1335
1474
  lower, lower_idx = tuple_gte_with_index(min - 1)
1336
- if lower.nil? then tuples << tuple
1337
- elsif (max + 1) < lower.first then tuples.insert(lower_idx, tuple)
1475
+ if lower.nil? then tuples << [min, max]
1476
+ elsif (max + 1) < lower.first then tuples.insert(lower_idx, [min, max])
1338
1477
  else tuple_coalesce(lower, lower_idx, min, max)
1339
1478
  end
1340
1479
  end
@@ -1367,6 +1506,7 @@ module Net
1367
1506
  # -------??=====lower====|--|====|---|====upper====|-- 7. delete until
1368
1507
  # -------??=====lower====|--|====|--|=====upper====|-- 8. delete and trim
1369
1508
  def tuple_subtract(tuple)
1509
+ modifying!
1370
1510
  min, max = tuple
1371
1511
  lower, idx = tuple_gte_with_index(min)
1372
1512
  if lower.nil? then nil # case 1.
@@ -1407,12 +1547,11 @@ module Net
1407
1547
  end
1408
1548
 
1409
1549
  def nz_number(num)
1410
- case num
1411
- when Integer, /\A[1-9]\d*\z/ then num = Integer(num)
1412
- else raise DataFormatError, "%p is not a valid nz-number" % [num]
1413
- end
1414
- NumValidator.ensure_nz_number(num)
1415
- num
1550
+ String === num && !/\A[1-9]\d*\z/.match?(num) and
1551
+ raise DataFormatError, "%p is not a valid nz-number" % [num]
1552
+ NumValidator.ensure_nz_number Integer num
1553
+ rescue TypeError # To catch errors from Integer()
1554
+ raise DataFormatError, $!.message
1416
1555
  end
1417
1556
 
1418
1557
  # intentionally defined after the class implementation
@@ -4,7 +4,7 @@ module Net
4
4
  class IMAP
5
5
  module StringPrep
6
6
 
7
- # Defined in RFC3491[https://tools.ietf.org/html/rfc3491], the +nameprep+
7
+ # Defined in RFC3491[https://www.rfc-editor.org/rfc/rfc3491], the +nameprep+
8
8
  # profile of "Stringprep" is:
9
9
  # >>>
10
10
  # used by the IDNA protocol for preparing domain names; it is not
@@ -4,11 +4,11 @@ module Net
4
4
  class IMAP
5
5
  module StringPrep
6
6
 
7
- # Defined in RFC-4505[https://tools.ietf.org/html/rfc4505] §3, The +trace+
7
+ # Defined in RFC-4505[https://www.rfc-editor.org/rfc/rfc4505] §3, The +trace+
8
8
  # profile of \StringPrep is used by the +ANONYMOUS+ \SASL mechanism.
9
9
  module Trace
10
10
 
11
- # Defined in RFC-4505[https://tools.ietf.org/html/rfc4505] §3.
11
+ # Defined in RFC-4505[https://www.rfc-editor.org/rfc/rfc4505] §3.
12
12
  STRINGPREP_PROFILE = "trace"
13
13
 
14
14
  # >>>
@@ -23,7 +23,7 @@ module Net
23
23
  # No Unicode normalization is required by this profile.
24
24
  NORMALIZATION = nil
25
25
 
26
- # From RFC-4505[https://tools.ietf.org/html/rfc4505] §3, The "trace"
26
+ # From RFC-4505[https://www.rfc-editor.org/rfc/rfc4505] §3, The "trace"
27
27
  # Profile of "Stringprep":
28
28
  # >>>
29
29
  # Characters from the following tables of [StringPrep] are prohibited:
@@ -47,7 +47,7 @@ module Net
47
47
 
48
48
  module_function
49
49
 
50
- # From RFC-4505[https://tools.ietf.org/html/rfc4505] §3, The "trace"
50
+ # From RFC-4505[https://www.rfc-editor.org/rfc/rfc4505] §3, The "trace"
51
51
  # Profile of "Stringprep":
52
52
  # >>>
53
53
  # The character repertoire of this profile is Unicode 3.2 [Unicode].