Module: Mailmate::EmlLookup

Defined in:
lib/mailmate/eml_lookup.rb

Overview

Map an ‘.eml` body-part ID to its absolute on-disk path.

Two implementations:

- The fast path uses MailMate's `#source` index
  (`Database.noindex/Headers/#source.{cache,offsets}`), which carries the
  `imap://account@host/path` URL for every indexed message. O(1) lookup.
- The fallback walks the IMAP tree with `Dir.glob`, the same shape as the
  `find -name <id>.eml` shell-out the original scripts used. Slower
  (seconds on a cold cache) but always works.

The fast path wins ~99% of the time. The fallback exists for messages MailMate hasn’t yet indexed (e.g. immediately after a fresh IMAP push) and for edge cases where ‘#source` is unreadable.

Class Method Summary collapse

Class Method Details

.eml_id_for_message_id(message_id) ⇒ Object

Reverse-lookup: given an RFC Message-ID (with or without angle brackets), return the local eml-id (integer) or nil. O(n) scan of the message-id index — fine for one-shot CLI lookups; cache the result if you need it repeatedly.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/mailmate/eml_lookup.rb', line 29

def self.eml_id_for_message_id(message_id)
  needle = message_id.to_s.strip
  return nil if needle.empty?

  candidates = needle.start_with?("<") && needle.end_with?(">") ?
                 [needle, needle[1..-2]] :
                 [needle, "<#{needle}>"]

  Mailmate::IndexReader.for("message-id").each_record do |eml_id, value|
    return eml_id if candidates.include?(value)
  end
  nil
rescue ArgumentError
  nil
end

.path_for(eml_id) ⇒ Object

Returns an absolute path string, or nil if not found.



21
22
23
# File 'lib/mailmate/eml_lookup.rb', line 21

def self.path_for(eml_id)
  via_index(eml_id) || via_glob(eml_id)
end

.resolve_id(input) ⇒ Object

Resolve an identifier that may be either an eml-id (all digits) or an RFC Message-ID (anything else) to a local eml-id.



47
48
49
50
51
# File 'lib/mailmate/eml_lookup.rb', line 47

def self.resolve_id(input)
  s = input.to_s.strip
  return s.to_i if s =~ /\A\d+\z/
  eml_id_for_message_id(s)
end

.source_url_for(eml_id) ⇒ Object

Lookup the ‘imap://…` URL recorded in `#source` for this eml_id. Returns nil if the index doesn’t have a record for it.



68
69
70
71
72
73
# File 'lib/mailmate/eml_lookup.rb', line 68

def self.source_url_for(eml_id)
  Mailmate::IndexReader.for("#source").value_for(eml_id)
rescue ArgumentError
  # #source index missing (non-default MailMate install? fresh sync?).
  nil
end

.url_to_path(url, eml_id) ⇒ Object

Convert an ‘imap://account@host/mailbox/path` URL to the on-disk absolute path of the .eml file. Internal but exposed for tests.



77
78
79
80
81
82
83
84
# File 'lib/mailmate/eml_lookup.rb', line 77

def self.url_to_path(url, eml_id)
  stripped = url.sub(%r{\Aimap://}, "")
  , mailbox_path = stripped.split("/", 2)
  return nil if .nil? || mailbox_path.nil? || mailbox_path.empty?

  mailbox_dirs = mailbox_path.split("/").map { |seg| "#{seg}.mailbox" }.join("/")
  File.join(Mailmate.config.imap_root, , mailbox_dirs, "Messages", "#{eml_id}.eml")
end

.via_glob(eml_id) ⇒ Object

Force the glob fallback.



61
62
63
64
# File 'lib/mailmate/eml_lookup.rb', line 61

def self.via_glob(eml_id)
  matches = Dir.glob("#{Mailmate.config.imap_root}/*/**/Messages/#{eml_id}.eml")
  matches.first
end

.via_index(eml_id) ⇒ Object

Force the index path (useful for tests and benchmarking).



54
55
56
57
58
# File 'lib/mailmate/eml_lookup.rb', line 54

def self.via_index(eml_id)
  url = source_url_for(eml_id)
  return nil if url.nil?
  url_to_path(url, eml_id)
end