Skip to content

Commit c91073f

Browse files
committed
✨ Add partial fetch modifier to uid_fetch
1 parent 6cfdb02 commit c91073f

File tree

2 files changed

+65
-3
lines changed

2 files changed

+65
-3
lines changed

lib/net/imap.rb

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ module Net
537537
# ==== RFC9394: +PARTIAL+
538538
# - Updates #search, #uid_search with the +PARTIAL+ return option which adds
539539
# ESearchResult#partial return data.
540-
# - TODO: Updates #uid_fetch with the +partial+ modifier.
540+
# - Updates #uid_fetch with the +partial+ modifier.
541541
#
542542
# == References
543543
#
@@ -2437,14 +2437,37 @@ def fetch(...)
24372437
end
24382438

24392439
# :call-seq:
2440-
# uid_fetch(set, attr, changedsince: nil) -> array of FetchData
2440+
# uid_fetch(set, attr, changedsince: nil, partial: nil) -> array of FetchData
24412441
#
24422442
# Sends a {UID FETCH command [IMAP4rev1 §6.4.8]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.4.8]
24432443
# to retrieve data associated with a message in the mailbox.
24442444
#
24452445
# Similar to #fetch, but the +set+ parameter contains unique identifiers
24462446
# instead of message sequence numbers.
24472447
#
2448+
# When #uid_fetch may also be given a +partial+ range, which can be used to
2449+
# limit the number of results. <em>Requires the +PARTIAL+
2450+
# capabability.</em> {[RFC9394]}[https://rfc-editor.org/rfc/rfc9394]
2451+
#
2452+
# For example:
2453+
#
2454+
# # Without partial, the size of the results may be unknown beforehand:
2455+
# results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS))
2456+
# # ... maybe wait for a long time ... and allocate a lot of memory ...
2457+
# results.size # => 0..2**32-1
2458+
# process results # may also take a long time and use a lot of memory...
2459+
#
2460+
# # Using partial, the results may be paginated:
2461+
# loop do
2462+
# results = imap.uid_fetch(next_uid_to_fetch.., %w(UID FLAGS),
2463+
# partial: 1..500)
2464+
# # fetch should return quickly and allocate little memory
2465+
# results.size # => 0..500
2466+
# break if results.empty?
2467+
# next_uid_to_fetch = results.last.uid + 1
2468+
# process results
2469+
# end
2470+
#
24482471
# >>>
24492472
# *Note:* Servers _MUST_ implicitly include the +UID+ message data item as
24502473
# part of any +FETCH+ response caused by a +UID+ command, regardless of
@@ -3412,8 +3435,26 @@ def search_internal(cmd, ...)
34123435
end
34133436
end
34143437

3415-
def fetch_internal(cmd, set, attr, mod = nil, changedsince: nil)
3438+
def partial_range(range)
3439+
case range
3440+
in /\a(?:\d+:\d+|-\d+:-\d+)\z/
3441+
range
3442+
in Range
3443+
minmax = range.minmax.map { Integer _1 }
3444+
if minmax.all?(1..2**32-1) || minmax.all?(-2**32..-1)
3445+
minmax.join(":")
3446+
else
3447+
raise ArgumentError, "invalid partial-range"
3448+
end
3449+
end
3450+
end
3451+
3452+
def fetch_internal(cmd, set, attr, mod = nil, partial: nil, changedsince: nil)
34163453
set = SequenceSet[set]
3454+
if partial
3455+
mod ||= []
3456+
mod << "PARTIAL" << partial_range(partial)
3457+
end
34173458
if changedsince
34183459
mod ||= []
34193460
mod << "CHANGEDSINCE" << Integer(changedsince)

test/net/imap/test_imap.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,27 @@ def test_enable
11641164
end
11651165
end
11661166

1167+
test "#uid_fetch with partial" do
1168+
with_fake_server select: "inbox" do |server, imap|
1169+
server.on("UID FETCH", &:done_ok)
1170+
imap.uid_fetch 1.., "FAST", partial: 1..500
1171+
assert_equal("RUBY0002 UID FETCH 1:* FAST (PARTIAL 1:500)",
1172+
server.commands.pop.raw.strip)
1173+
imap.uid_fetch 1.., "FAST", partial: 1...501
1174+
assert_equal("RUBY0003 UID FETCH 1:* FAST (PARTIAL 1:500)",
1175+
server.commands.pop.raw.strip)
1176+
imap.uid_fetch 1.., "FAST", partial: -500..-1
1177+
assert_equal("RUBY0004 UID FETCH 1:* FAST (PARTIAL -500:-1)",
1178+
server.commands.pop.raw.strip)
1179+
imap.uid_fetch 1.., "FAST", partial: -500...-1
1180+
assert_equal("RUBY0005 UID FETCH 1:* FAST (PARTIAL -500:-2)",
1181+
server.commands.pop.raw.strip)
1182+
imap.uid_fetch 1.., "FAST", partial: 1..20, changedsince: 1234
1183+
assert_equal("RUBY0006 UID FETCH 1:* FAST (PARTIAL 1:20 CHANGEDSINCE 1234)",
1184+
server.commands.pop.raw.strip)
1185+
end
1186+
end
1187+
11671188
test "#store with unchangedsince" do
11681189
with_fake_server select: "inbox" do |server, imap|
11691190
server.on("STORE", &:done_ok)

0 commit comments

Comments
 (0)