Skip to content

Commit

Permalink
🔀 Merge pull request #319 from ruby/sequence-set-input-validation
Browse files Browse the repository at this point in the history
💥 SequenceSet input validation for Set, Array, and enumerables
  • Loading branch information
nevans authored Sep 13, 2024
2 parents c198fcc + b01d864 commit 5907cd9
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 13 deletions.
6 changes: 3 additions & 3 deletions lib/net/imap/data_encoding.rb
Original file line number Diff line number Diff line change
Expand Up @@ -186,23 +186,23 @@ def valid_mod_sequence_value?(num)

# Ensure argument is 'number' or raise DataFormatError
def ensure_number(num)
return if valid_number?(num)
return num if valid_number?(num)

msg = "number must be unsigned 32-bit integer: #{num}"
raise DataFormatError, msg
end

# Ensure argument is 'nz_number' or raise DataFormatError
def ensure_nz_number(num)
return if valid_nz_number?(num)
return num if valid_nz_number?(num)

msg = "nz_number must be non-zero unsigned 32-bit integer: #{num}"
raise DataFormatError, msg
end

# Ensure argument is 'mod_sequence_value' or raise DataFormatError
def ensure_mod_sequence_value(num)
return if valid_mod_sequence_value?(num)
return num if valid_mod_sequence_value?(num)

msg = "mod_sequence_value must be unsigned 64-bit integer: #{num}"
raise DataFormatError, msg
Expand Down
20 changes: 10 additions & 10 deletions lib/net/imap/sequence_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ class IMAP
#
# SequenceSet.new may receive a single optional argument: a non-zero 32 bit
# unsigned integer, a range, a <tt>sequence-set</tt> formatted string,
# another sequence set, or an enumerable containing any of these.
# another sequence set, a Set (containing only numbers), or an Array
# containing any of these (array inputs may be nested).
#
# set = Net::IMAP::SequenceSet.new(1)
# set.valid_string #=> "1"
Expand Down Expand Up @@ -289,8 +290,7 @@ class SequenceSet
private_constant :STAR_INT, :STARS

COERCIBLE = ->{ _1.respond_to? :to_sequence_set }
ENUMABLE = ->{ _1.respond_to?(:each) && _1.respond_to?(:empty?) }
private_constant :COERCIBLE, :ENUMABLE
private_constant :COERCIBLE

class << self

Expand Down Expand Up @@ -1271,7 +1271,8 @@ def input_to_tuples(obj)
when *STARS, Integer, Range then [input_to_tuple(obj)]
when String then str_to_tuples obj
when SequenceSet then obj.tuples
when ENUMABLE then obj.flat_map { input_to_tuples _1 }
when Set then obj.map { to_tuple_int _1 }
when Array then obj.flat_map { input_to_tuples _1 }
when nil then []
else
raise DataFormatError,
Expand Down Expand Up @@ -1406,12 +1407,11 @@ def range_gte_to(num)
end

def nz_number(num)
case num
when Integer, /\A[1-9]\d*\z/ then num = Integer(num)
else raise DataFormatError, "%p is not a valid nz-number" % [num]
end
NumValidator.ensure_nz_number(num)
num
String === num && !/\A[1-9]\d*\z/.match?(num) and
raise DataFormatError, "%p is not a valid nz-number" % [num]
NumValidator.ensure_nz_number Integer num
rescue TypeError # To catch errors from Integer()
raise DataFormatError, $!.message
end

# intentionally defined after the class implementation
Expand Down
2 changes: 2 additions & 0 deletions test/net/imap/test_sequence_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ def compare_to_reference_set(nums, set, seqset)
assert_raise DataFormatError do SequenceSet.new "2 " end
assert_raise DataFormatError do SequenceSet.new "2," end
assert_raise DataFormatError do SequenceSet.new Time.now end
assert_raise DataFormatError do SequenceSet.new Set[1, [2]] end
assert_raise DataFormatError do SequenceSet.new Set[1..20] end
end

test ".new, input may be empty" do
Expand Down

0 comments on commit 5907cd9

Please sign in to comment.