Skip to content

Commit 9255757

Browse files
committed
✨ Add partial fetch modifier to uid_fetch
1 parent fa4502d commit 9255757

File tree

2 files changed

+66
-9
lines changed

2 files changed

+66
-9
lines changed

lib/net/imap.rb

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,12 +2424,12 @@ def uid_search(...)
24242424
# {[RFC7162]}[https://tools.ietf.org/html/rfc7162] in order to use the
24252425
# +changedsince+ argument. Using +changedsince+ implicitly enables the
24262426
# +CONDSTORE+ extension.
2427-
def fetch(set, attr, mod = nil, changedsince: nil)
2428-
fetch_internal("FETCH", set, attr, mod, changedsince: changedsince)
2427+
def fetch(...)
2428+
fetch_internal("FETCH", ...)
24292429
end
24302430

24312431
# :call-seq:
2432-
# uid_fetch(set, attr, changedsince: nil) -> array of FetchData
2432+
# uid_fetch(set, attr, changedsince: nil, partial: nil) -> array of FetchData
24332433
#
24342434
# Sends a {UID FETCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
24352435
# to retrieve data associated with a message in the mailbox.
@@ -2446,13 +2446,44 @@ def fetch(set, attr, mod = nil, changedsince: nil)
24462446
#
24472447
# +changedsince+ (optional) behaves the same as with #fetch.
24482448
#
2449+
# +partial+ is an optional range to limit the number of results returned.
2450+
# It's useful when +set+ contains an unknown number of messages.
2451+
# <tt>1..500</tt> returns the first 500 messages in +set+ (in mailbox
2452+
# order), <tt>501..1000</tt> the second 500, and so on. +partial+ may also
2453+
# be negative: <tt>-500..-1</tt> selects the last 500 messages in +set+.
2454+
# <em>Requires the +PARTIAL+ capabability.</em>
2455+
# {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
2456+
#
2457+
# For example:
2458+
#
2459+
# # Without partial, the size of the results may be unknown beforehand:
2460+
# results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS))
2461+
# # ... maybe wait for a long time ... and allocate a lot of memory ...
2462+
# results.size # => 0..2**32-1
2463+
# process results # may also take a long time and use a lot of memory...
2464+
#
2465+
# # Using partial, the results may be paginated:
2466+
# loop do
2467+
# results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS),
2468+
# partial: 1..500)
2469+
# # fetch should return quickly and allocate little memory
2470+
# results.size # => 0..500
2471+
# break if results.empty?
2472+
# next_uid_to_fetch = results.last.uid + 1
2473+
# process results
2474+
# end
2475+
#
24492476
# Related: #fetch, FetchData
24502477
#
24512478
# ==== Capabilities
24522479
#
2453-
# Same as #fetch.
2454-
def uid_fetch(set, attr, mod = nil, changedsince: nil)
2455-
fetch_internal("UID FETCH", set, attr, mod, changedsince: changedsince)
2480+
# The server's capabilities must include +PARTIAL+
2481+
# {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394] in order to use the
2482+
# +partial+ argument.
2483+
#
2484+
# Otherwise, the same as #fetch.
2485+
def uid_fetch(...)
2486+
fetch_internal("UID FETCH", ...)
24562487
end
24572488

24582489
# :call-seq:
@@ -3398,7 +3429,12 @@ def search_internal(cmd, ...)
33983429
end
33993430
end
34003431

3401-
def fetch_internal(cmd, set, attr, mod = nil, changedsince: nil)
3432+
def fetch_internal(cmd, set, attr, mod = nil, partial: nil, changedsince: nil)
3433+
set = SequenceSet[set]
3434+
if partial
3435+
mod ||= []
3436+
mod << "PARTIAL" << PartialRange[partial]
3437+
end
34023438
if changedsince
34033439
mod ||= []
34043440
mod << "CHANGEDSINCE" << Integer(changedsince)
@@ -3415,9 +3451,9 @@ def fetch_internal(cmd, set, attr, mod = nil, changedsince: nil)
34153451
synchronize do
34163452
clear_responses("FETCH")
34173453
if mod
3418-
send_command(cmd, SequenceSet.new(set), attr, mod)
3454+
send_command(cmd, set, attr, mod)
34193455
else
3420-
send_command(cmd, SequenceSet.new(set), attr)
3456+
send_command(cmd, set, attr)
34213457
end
34223458
clear_responses("FETCH")
34233459
end

test/net/imap/test_imap.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,27 @@ def test_enable
12141214
end
12151215
end
12161216

1217+
test "#uid_fetch with partial" do
1218+
with_fake_server select: "inbox" do |server, imap|
1219+
server.on("UID FETCH", &:done_ok)
1220+
imap.uid_fetch 1.., "FAST", partial: 1..500
1221+
assert_equal("RUBY0002 UID FETCH 1:* FAST (PARTIAL 1:500)",
1222+
server.commands.pop.raw.strip)
1223+
imap.uid_fetch 1.., "FAST", partial: 1...501
1224+
assert_equal("RUBY0003 UID FETCH 1:* FAST (PARTIAL 1:500)",
1225+
server.commands.pop.raw.strip)
1226+
imap.uid_fetch 1.., "FAST", partial: -500..-1
1227+
assert_equal("RUBY0004 UID FETCH 1:* FAST (PARTIAL -500:-1)",
1228+
server.commands.pop.raw.strip)
1229+
imap.uid_fetch 1.., "FAST", partial: -500...-1
1230+
assert_equal("RUBY0005 UID FETCH 1:* FAST (PARTIAL -500:-2)",
1231+
server.commands.pop.raw.strip)
1232+
imap.uid_fetch 1.., "FAST", partial: 1..20, changedsince: 1234
1233+
assert_equal("RUBY0006 UID FETCH 1:* FAST (PARTIAL 1:20 CHANGEDSINCE 1234)",
1234+
server.commands.pop.raw.strip)
1235+
end
1236+
end
1237+
12171238
test "#store with unchangedsince" do
12181239
with_fake_server select: "inbox" do |server, imap|
12191240
server.on("STORE", &:done_ok)

0 commit comments

Comments
 (0)