Module: Mailmate Private

Defined in:
lib/mailmate.rb,
lib/mailmate.rb,
lib/mailmate/ast.rb,
lib/mailmate/mcp.rb,
lib/mailmate/lexer.rb,
lib/mailmate/config.rb,
lib/mailmate/parser.rb,
lib/mailmate/message.rb,
lib/mailmate/mid_url.rb,
lib/mailmate/version.rb,
lib/mailmate/cli/open.rb,
lib/mailmate/cli/send.rb,
lib/mailmate/cli/tags.rb,
lib/mailmate/identity.rb,
lib/mailmate/evaluator.rb,
lib/mailmate/operators.rb,
lib/mailmate/attributes.rb,
lib/mailmate/cli/modify.rb,
lib/mailmate/cli/search.rb,
lib/mailmate/eml_lookup.rb,
lib/mailmate/cli/message.rb,
lib/mailmate/part_lookup.rb,
lib/mailmate/cli/discover.rb,
lib/mailmate/index_reader.rb,
lib/mailmate/var_resolver.rb,
lib/mailmate/cli/mailboxes.rb,
lib/mailmate/header_reader.rb,
lib/mailmate/mailbox_graph.rb,
lib/mailmate/platform_error.rb,
lib/mailmate/source_resolver.rb,
lib/mailmate/duplicate_scanner.rb,
lib/mailmate/filter_classifier.rb,
lib/mailmate/applescript_driver.rb

Overview

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.

FilterClassifier — walks an AST and answers questions about what’s required to evaluate it. Used by ‘mailmate-search` to pick the cheapest `.eml` loading tier per query:

:index   — every path is index-backed; never open the .eml at all.
:header  — every path resolves from headers (or index); read header block only.
:full    — at least one path needs body content; full Mail.read.

Also extracts ASCII string literals from top-level AND-chained header-path comparisons so the raw-bytes pre-filter can short-circuit without parsing.

Defined Under Namespace

Modules: AST, Attributes, CLI, DuplicateScanner, EmlLookup, FilterClassifier, HeaderReader, Identity, Lexer, MCP, MidUrl, Operators, PartLookup Classes: AppleScriptDriver, Config, Evaluator, IndexReader, MailboxGraph, Message, Parser, PlatformError, SourceResolver, VarResolver

Constant Summary collapse

VERSION =
"1.2.0"

Class Method Summary collapse

Class Method Details

.compile_filter(str) ⇒ Object



77
78
79
# File 'lib/mailmate.rb', line 77

def self.compile_filter(str)
  Parser.parse(Lexer.lex(str))
end

.configObject

Convenience: ‘Mailmate.config.imap_root` etc. Triggers first-run discovery if `~/.config/mailmate/config.yml` doesn’t exist yet (interactive on a TTY, warn-and-continue otherwise).



133
134
135
136
# File 'lib/mailmate/config.rb', line 133

def self.config
  ensure_configured! if respond_to?(:ensure_configured!)
  Config.instance
end

.ensure_configured!(stdin: $stdin, stderr: $stderr) ⇒ Object

First-run bootstrap. If ~/.config/mailmate/config.yml is missing, interactively run ‘mmdiscover` (TTY callers) or warn once and continue with built-in defaults (non-TTY: MCP server, scripts, cron jobs).

Idempotent: only checks once per process. Triggered automatically by ‘Mailmate.config`, so every CLI and the MCP server pick it up without extra wiring.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/mailmate.rb', line 62

def self.ensure_configured!(stdin: $stdin, stderr: $stderr)
  return if @configured_checked
  @configured_checked = true
  return if File.exist?(Config::DEFAULT_CONFIG_PATH)

  if stdin.tty?
    stderr.puts "mailmate: no config found at #{Config::DEFAULT_CONFIG_PATH} — running first-run setup (mmdiscover)."
    require_relative "mailmate/cli/discover"
    Mailmate::CLI::Discover.run([])
    Mailmate::Config.reload!
  else
    stderr.puts "mailmate: no config at #{Config::DEFAULT_CONFIG_PATH}; using built-in defaults. Run `mmdiscover` once in a terminal for identity-aware output."
  end
end

.localize(time) ⇒ Object

Convert a Time to the configured display zone. If ‘display_timezone` is set in config, use it as the offset (e.g. “-07:00”). Otherwise fall back to the system local zone (which honors macOS’s DST rules, so Mountain users get MDT in summer and MST in winter).



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

def self.localize(time)
  return nil unless time
  t = time.respond_to?(:to_time) ? time.to_time : time
  zone = config.display_timezone
  if zone && !zone.empty?
    t.getlocal(zone)
  else
    t.getlocal
  end
rescue StandardError
  time
end