# frozen_string_literal: true
module Net
class IMAP
# An "extended search" response (+ESEARCH+). ESearchResult should be
# returned (instead of SearchResult) by IMAP#search, IMAP#uid_search,
# IMAP#sort, and IMAP#uid_sort under any of the following conditions:
#
# * Return options were specified for IMAP#search or IMAP#uid_search.
# The server must support a search extension which allows
# RFC4466[https://www.rfc-editor.org/rfc/rfc4466.html] +return+ options,
# such as +ESEARCH+, +PARTIAL+, or +IMAP4rev2+.
# * Return options were specified for IMAP#sort or IMAP#uid_sort.
# The server must support the +ESORT+ extension
# {[RFC5267]}[https://www.rfc-editor.org/rfc/rfc5267.html#section-3].
#
# *NOTE:* IMAP#search and IMAP#uid_search do not support +ESORT+ yet.
# * The server supports +IMAP4rev2+ but _not_ +IMAP4rev1+, or +IMAP4rev2+
# has been enabled. +IMAP4rev2+ requires +ESEARCH+ results.
#
# Note that some servers may claim to support a search extension which
# requires an +ESEARCH+ result, such as +PARTIAL+, but still only return a
# +SEARCH+ result when +return+ options are specified.
#
# Some search extensions may result in the server sending ESearchResult
# responses after the initiating command has completed. Use
# IMAP#add_response_handler to handle these responses.
class ESearchResult < Data.define(:tag, :uid, :data)
def initialize(tag: nil, uid: nil, data: nil)
tag => String | nil; tag = -tag if tag
uid => true | false | nil; uid = !!uid
data => Array | nil; data ||= []; data.freeze
super
end
# :call-seq: to_a -> Array of integers
#
# When either #all or #partial contains a SequenceSet of message sequence
# numbers or UIDs, +to_a+ returns that set as an array of integers.
#
# When both #all and #partial are +nil+, either because the server
# returned no results or because +ALL+ and +PARTIAL+ were not included in
# the IMAP#search +RETURN+ options, #to_a returns an empty array.
#
# Note that SearchResult also implements +to_a+, so it can be used without
# checking if the server returned +SEARCH+ or +ESEARCH+ data.
def to_a; all&.numbers || partial&.to_a || [] end
##
# attr_reader: tag
#
# The tag string for the command that caused this response to be returned.
#
# When +nil+, this response was not caused by a particular command.
##
# attr_reader: uid
#
# Indicates whether #data in this response refers to UIDs (when +true+) or
# to message sequence numbers (when +false+).
##
alias uid? uid
##
# attr_reader: data
#
# Search return data, as an array of [name, value] pairs. Most
# return data corresponds to a search +return+ option with the same name.
#
# Note that some return data names may be used more than once per result.
#
# This data can be more simply retrieved by #min, #max, #all, #count,
# #modseq, and other methods.
# :call-seq: min -> integer or nil
#
# The lowest message number/UID that satisfies the SEARCH criteria.
#
# Returns +nil+ when the associated search command has no results, or when
# the +MIN+ return option wasn't specified.
#
# Requires +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.1] or
# +IMAP4rev2+ {[RFC9051]}[https://www.rfc-editor.org/rfc/rfc9051.html#section-7.3.4].
def min; data.assoc("MIN")&.last end
# :call-seq: max -> integer or nil
#
# The highest message number/UID that satisfies the SEARCH criteria.
#
# Returns +nil+ when the associated search command has no results, or when
# the +MAX+ return option wasn't specified.
#
# Requires +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.1] or
# +IMAP4rev2+ {[RFC9051]}[https://www.rfc-editor.org/rfc/rfc9051.html#section-7.3.4].
def max; data.assoc("MAX")&.last end
# :call-seq: all -> sequence set or nil
#
# A SequenceSet containing all message sequence numbers or UIDs that
# satisfy the SEARCH criteria.
#
# Returns +nil+ when the associated search command has no results, or when
# the +ALL+ return option was not specified but other return options were.
#
# Requires +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.1] or
# +IMAP4rev2+ {[RFC9051]}[https://www.rfc-editor.org/rfc/rfc9051.html#section-7.3.4].
#
# See also: #to_a
def all; data.assoc("ALL")&.last end
# :call-seq: count -> integer or nil
#
# Returns the number of messages that satisfy the SEARCH criteria.
#
# Returns +nil+ when the associated search command has no results, or when
# the +COUNT+ return option wasn't specified.
#
# Requires +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.1] or
# +IMAP4rev2+ {[RFC9051]}[https://www.rfc-editor.org/rfc/rfc9051.html#section-7.3.4].
def count; data.assoc("COUNT")&.last end
# :call-seq: modseq -> integer or nil
#
# The highest +mod-sequence+ of all messages being returned.
#
# Returns +nil+ when the associated search command has no results, or when
# the +MODSEQ+ search criterion wasn't specified.
#
# Note that there is no search +return+ option for +MODSEQ+. It will be
# returned whenever the +CONDSTORE+ extension has been enabled. Using the
# +MODSEQ+ search criteria will implicitly enable +CONDSTORE+.
#
# Requires +CONDSTORE+ {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html]
# and +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.2].
def modseq; data.assoc("MODSEQ")&.last end
# Returned by ESearchResult#partial.
#
# Requires +PARTIAL+ {[RFC9394]}[https://www.rfc-editor.org/rfc/rfc9394.html]
# or CONTEXT=SEARCH/CONTEXT=SORT
# {[RFC5267]}[https://www.rfc-editor.org/rfc/rfc5267.html]
#
# See also: #to_a
class PartialResult < Data.define(:range, :results)
def initialize(range:, results:)
range => Range
results = SequenceSet[results] unless results.nil?
super
end
##
# method: range
# :call-seq: range -> range
##
# method: results
# :call-seq: results -> sequence set or nil
# Converts #results to an array of integers.
#
# See also: ESearchResult#to_a.
def to_a; results&.numbers || [] end
end
# :call-seq: partial -> PartialResult or nil
#
# A PartialResult containing a subset of the message sequence numbers or
# UIDs that satisfy the SEARCH criteria.
#
# Requires +PARTIAL+ {[RFC9394]}[https://www.rfc-editor.org/rfc/rfc9394.html]
# or CONTEXT=SEARCH/CONTEXT=SORT
# {[RFC5267]}[https://www.rfc-editor.org/rfc/rfc5267.html]
#
# See also: #to_a
def partial; data.assoc("PARTIAL")&.last end
end
end
end