Skip to content

Commit

Permalink
🔀 Merge pull request #351 from ruby/search-coerce-sequence_set
Browse files Browse the repository at this point in the history
✨ Coerce `Set`, `:*`, `#to_sequence_set` search args into sequence-set
  • Loading branch information
nevans authored Nov 8, 2024
2 parents 950a992 + 6413656 commit 799cc94
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 12 deletions.
24 changes: 18 additions & 6 deletions lib/net/imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1944,8 +1944,12 @@ def uid_expunge(uid_set)
#
# * When +criteria+ is an array, each member is a +SEARCH+ command argument:
# * Any SequenceSet sends SequenceSet#valid_string.
# +Range+, <tt>-1</tt>, and nested +Array+ elements are converted to
# SequenceSet.
# These types are converted to SequenceSet for validation and encoding:
# * +Set+
# * +Range+
# * <tt>-1</tt> and +:*+ -- both translate to <tt>*</tt>
# * responds to +#to_sequence_set+
# * nested +Array+
# * Any +String+ is sent verbatim when it is a valid \IMAP atom,
# and encoded as an \IMAP quoted or literal string otherwise.
# * Any other +Integer+ (besides <tt>-1</tt>) will be sent as +#to_s+.
Expand Down Expand Up @@ -3191,13 +3195,21 @@ def thread_internal(cmd, algorithm, search_keys, charset)

def normalize_searching_criteria(criteria)
return RawData.new(criteria) if criteria.is_a?(String)
criteria.map do |i|
case i
when -1, Range, Array
SequenceSet.new(i)
criteria.map {|i|
if coerce_search_arg_to_seqset?(i)
SequenceSet[i]
else
i
end
}
end

def coerce_search_arg_to_seqset?(obj)
case obj
when Set, -1, :* then true
when Range then true
when Array then true
else obj.respond_to?(:to_sequence_set)
end
end

Expand Down
5 changes: 1 addition & 4 deletions lib/net/imap/sequence_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,7 @@ class SequenceSet

# valid inputs for "*"
STARS = [:*, ?*, -1].freeze
private_constant :STAR_INT, :STARS

COERCIBLE = ->{ _1.respond_to? :to_sequence_set }
private_constant :COERCIBLE
private_constant :STARS

class << self

Expand Down
30 changes: 28 additions & 2 deletions test/net/imap/test_imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1206,10 +1206,36 @@ def test_unselect
end

server.on "SEARCH", &search_resp
assert_equal search_result, imap.search(["subject", "hello",
assert_equal search_result, imap.search(["subject", "hello world",
[1..5, 8, 10..-1]])
cmd = server.commands.pop
assert_equal ["SEARCH", "subject hello 1:5,8,10:*"], [cmd.name, cmd.args]
assert_equal(
["SEARCH",'subject "hello world" 1:5,8,10:*'],
[cmd.name, cmd.args]
)

imap.search(["OR", 1..1000, -1, "UID", 12345..-1])
assert_equal "OR 1:1000 * UID 12345:*", server.commands.pop.args

imap.search([1..1000, "UID", 12345..])
assert_equal "1:1000 UID 12345:*", server.commands.pop.args

# Unfortunately, we can't send every sequence-set string directly
imap.search(["SUBJECT", "1,*"])
assert_equal 'SUBJECT "1,*"', server.commands.pop.args

imap.search(["subject", "hello", Set[1, 2, 3, 4, 5, 8, *(10..100)]])
assert_equal "subject hello 1:5,8,10:100", server.commands.pop.args

imap.search([:*])
assert_equal "*", server.commands.pop.args

seqset_coercible = Object.new
def seqset_coercible.to_sequence_set
Net::IMAP::SequenceSet[1..9]
end
imap.search([seqset_coercible])
assert_equal "1:9", server.commands.pop.args

server.on "UID SEARCH", &search_resp
assert_equal search_result, imap.uid_search(["subject", "hello",
Expand Down

0 comments on commit 799cc94

Please sign in to comment.