Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💥 SequenceSet input validation for Set, Array, and enumerables #319

Merged
merged 3 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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