Module: Mailmate::FilterClassifier Private

Defined in:
lib/mailmate/filter_classifier.rb

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Constant Summary collapse

INDEX_BACKED_HEADS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Heads served by an on-disk index in Database.noindex/Headers/<name>.

%w[
  #flags ##tags
  #date #date-received #date-sent #date-last-viewed
].freeze
HEADER_HEADS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Heads that resolve from .eml header bytes (vs. body).

%w[
  from to cc bcc reply-to sender resent-from resent-to resent-cc resent-bcc
  subject list-id message-id in-reply-to references
  x-mailer user-agent x-newsreader received delivered-to
  #recipient #any-address #mailer ##thread-id
].freeze
BODY_HEADS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Heads that require the body to resolve.

%w[#unquoted #quoted #common #commonplus].freeze
PREFILTER_HEADS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Heads whose literals appear textually in the .eml header bytes (so they’re safe pre-filter candidates).

%w[
  from to cc bcc reply-to sender
  subject list-id message-id in-reply-to references
  x-mailer user-agent x-newsreader
  #recipient #any-address #mailer
].freeze

Class Method Summary collapse

Class Method Details

.collect_path_heads(ast) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

—- internals —-



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/mailmate/filter_classifier.rb', line 81

def self.collect_path_heads(ast)
  heads = []
  walk(ast) do |n|
    case n
    when AST::CompareNode, AST::ExistsNode
      heads << n.path[0]
    when AST::VarRefNode
      # var refs are evaluated by walking the referenced mailbox,
      # which is its own search. The OUTER caller doesn't need to load
      # body for a var ref — only headers/index, depending on the
      # path's head on the LHS side. The inner mailbox walk classifies
      # itself when it runs (via VarResolver's own header-only scan).
    end
  end
  heads
end

.combine_tiers(*tiers) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Combine multiple tiers; the strictest wins. (full > header > index)



65
66
67
68
69
# File 'lib/mailmate/filter_classifier.rb', line 65

def self.combine_tiers(*tiers)
  return :full   if tiers.include?(:full)
  return :header if tiers.include?(:header)
  :index
end

.header_literals(ast) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

ASCII literals from top-level AND-chained header-path comparisons, which are guaranteed to appear (substring-wise) in any matching message’s raw header bytes. Skips OR branches (literals there are alternatives, not requirements) and skips negated comparisons.



75
76
77
# File 'lib/mailmate/filter_classifier.rb', line 75

def self.header_literals(ast)
  walk_top_and(ast).flat_map { |node| literal_from(node) }.compact.uniq
end

.literal_from(node) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



113
114
115
116
117
118
119
120
121
# File 'lib/mailmate/filter_classifier.rb', line 113

def self.literal_from(node)
  return [] unless node.is_a?(AST::CompareNode)
  return [] if %w[!= !~].include?(node.op)
  return [] unless PREFILTER_HEADS.include?(node.path[0])
  return [] unless node.value.is_a?(AST::LiteralStringNode)
  s = node.value.value
  return [] unless s.bytesize >= 3 && s.ascii_only?
  [s.downcase]
end

.tier(ast) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns one of :index, :header, :full.



48
49
50
51
52
53
# File 'lib/mailmate/filter_classifier.rb', line 48

def self.tier(ast)
  heads = collect_path_heads(ast)
  return :full   if heads.any? { |h| BODY_HEADS.include?(h) }
  return :index  if heads.all? { |h| INDEX_BACKED_HEADS.include?(h) }
  :header
end

.tier_for_paths(paths) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Same idea but for a list of attribute paths (e.g. fields the user wants output, or paths the user-search specs reference).



57
58
59
60
61
62
# File 'lib/mailmate/filter_classifier.rb', line 57

def self.tier_for_paths(paths)
  heads = paths.map { |p| Array(p).first }.compact
  return :full   if heads.any? { |h| BODY_HEADS.include?(h) }
  return :index  if !heads.empty? && heads.all? { |h| INDEX_BACKED_HEADS.include?(h) }
  :header
end

.walk(ast) {|ast| ... } ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Yields:

  • (ast)


98
99
100
101
102
103
104
# File 'lib/mailmate/filter_classifier.rb', line 98

def self.walk(ast, &blk)
  yield ast
  case ast
  when AST::AndNode, AST::OrNode then ast.children.each { |c| walk(c, &blk) }
  when AST::NotNode then walk(ast.child, &blk)
  end
end

.walk_top_and(ast) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



106
107
108
109
110
111
# File 'lib/mailmate/filter_classifier.rb', line 106

def self.walk_top_and(ast)
  case ast
  when AST::AndNode then ast.children.flat_map { |c| walk_top_and(c) }
  else [ast]
  end
end