Skip to content

feat(gmail): add gmail render command and read -o markdown - #1516

Merged
newhoggy merged 3 commits into
mainfrom
issue-1513-gmail-render-eml-markdown
Aug 7, 2026
Merged

feat(gmail): add gmail render command and read -o markdown#1516
newhoggy merged 3 commits into
mainfrom
issue-1513-gmail-render-eml-markdown

Conversation

@newhoggy

@newhoggy newhoggy commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Test plan

  • cargo test --workspace --no-fail-fast — all pass except the pre-existing, environment-unrelated tests/daemon_test.rs lifecycle flake (confirmed reproducing in isolation, no daemon files touched by this branch)
  • cargo clippy --all-targets -- -D warnings clean
  • cargo fmt --check clean
  • update-snapshots skill run; tests/snapshots/integration_test__help_all_output.snap reviewed and accepted

gmail sync archives full-fidelity, byte-exact .eml files (ADR-0064), but
nothing in the codebase turned one back into something readable:
raw_message::extract_headers never decodes RFC 2047 encoded-word
subjects, attachments.rs's real MIME parser only ever extracted
attachment files, and gmail read's renderers never printed the body at
all.

Adds src/gmail/render.rs::render_markdown(raw) -> String, a sibling of
attachments.rs rather than an extension of raw_message.rs's
line-oriented scanner -- the same "needs real MIME parsing" reasoning
that module's own doc comment already gives for attachment bytes
applies at least as strongly here, plus RFC 2047 decoding that scanner
explicitly skips. Renders a header block (Subject/From/To/Cc/Date/
Message-Id/In-Reply-To/References, RFC 2047-decoded via mail-parser's
own header accessors -- no new decoding logic needed), the body (a
genuine text/plain part preferred; text/html converted to Markdown via
the new htmd crate otherwise, chosen over plain HTML-to-text since the
output format already is Markdown), and a bullet list of attachment
filenames (listed, never embedded -- this is a readable rendering, not
an export). Never fails: an unparseable message degrades to a short
placeholder rather than erroring, so one bad file can't abort a batch
caller.

omni-dev gmail render <PATH.eml>... [--out-dir DIR] [-o FORMAT]
(src/cli/gmail/render.rs) is dispatched alongside sync-all/
extract-attachments's no-client special cases -- purely local, no
GmailClient, no network, --account rejected outright. Deliberately
takes bare file paths rather than a Manifest/--archive-dir + message
id, so it works on any .eml file with no dependency on this tool having
synced the mailbox at all. Default -o table prints each input's
rendered Markdown straight to stdout (multiple inputs separated by a
--- thematic break, and no trailing summary line in the all-success
case, so `gmail render *.eml > combined.md` stays clean); --out-dir DIR
instead writes one .md file per input (named after its stem) and
prints a Saved to: line per file; -o json/yaml/yamls/jsonl emit one
structured record per input via the shared blanket Vec<T>
JsonlSerialize impl. A per-file read/write failure is recorded against
that file rather than aborting the batch, though the command still
exits non-zero if any file failed.

gmail read -o markdown (ReadOutputFormat, src/cli/gmail/read.rs) reuses
render_markdown against a live message, always fetching format=raw
regardless of --detail (rendering needs the full raw MIME structure).
Kept as a read-local enum extending the shared OutputFormat with a
Markdown variant, rather than adding Markdown to the crate-wide
OutputFormat every other CLI surface's -o renders generically via
Serialize -- a Markdown variant only makes sense for a MIME message.

Quote-folding and a Manifest-aware --archive-dir --all batch mode were
raised as open questions and deliberately deferred to #1514 and #1515
rather than blocking this issue.

Closes #1513
- docs/gmail.md gains a new "Render" section (Table of Contents
  included) following the Extract attachments style, plus an -o
  markdown paragraph under Messages and a cross-reference from Sync's
  Header fields paragraph -- the one that documents the RFC 2047
  non-decoding gap -- pointing at gmail render/gmail read -o markdown
  as the fix for reading such a message properly. Output formats gains
  a note that gmail read additionally accepts -o markdown.
- CHANGELOG.md's Unreleased/Added section gains an entry for #1513,
  matching the existing Gmail feature entries.
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Coverage

Total: 97.32% 🟢 0.01 pp vs main

Comparing 405da8d..b49787a (merge-base → PR head)

File Before After Δ
src/cli/gmail/render.rs 100% 🆕 new
src/gmail/render.rs 100% 🆕 new
src/cli/gmail/read.rs 100% 99.82% 🔴 -0.18 pp
src/cli/gmail.rs 98.23% 98.14% 🔴 -0.08 pp

Patch coverage

Patch: 99.72% (700/702 new lines covered)

File Patch Uncovered new lines
src/cli/gmail.rs 97.62% (41/42) 169
src/cli/gmail/read.rs 99.55% (223/224) 89
src/cli/gmail/render.rs 100% (236/236)
src/gmail/render.rs 100% (200/200)
Uncovered new lines (2)
  • src/cli/gmail.rs:169
  • src/cli/gmail/read.rs:89

📦 Full per-file coverage summary · run summary

Adds tests for the branches the coverage bot flagged as uncovered:
gmail read -o yaml/yamls/jsonl, the render fs::write error path,
md_filename's no-stem fallback, render_report_text's saved_to and
empty-entry branches, gmail render's In-Reply-To/References headers,
bare-display-name addresses, and attachment-only bodies, and
body_markdown's two content-type/body-mismatch fallback branches
(constructed directly against synthetic Message values, since
mail-parser's real parser never produces that mismatch).
@newhoggy
newhoggy force-pushed the issue-1513-gmail-render-eml-markdown branch from 3fd4165 to b49787a Compare August 7, 2026 12:41
@newhoggy
newhoggy merged commit d02fedb into main Aug 7, 2026
18 checks passed
@newhoggy
newhoggy deleted the issue-1513-gmail-render-eml-markdown branch August 7, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(gmail): render archived .eml messages to human-readable Markdown

1 participant