Skip to content

feat(gmail): add +read helper for clean text extraction#502

Open
dumko2001 wants to merge 5 commits intogoogleworkspace:mainfrom
dumko2001:feature/issue-438-gmail-read
Open

feat(gmail): add +read helper for clean text extraction#502
dumko2001 wants to merge 5 commits intogoogleworkspace:mainfrom
dumko2001:feature/issue-438-gmail-read

Conversation

@dumko2001
Copy link

Description

Add a Gmail +read helper to abstract MIME tree walking and base64 decoding for readable text extraction. This is specifically designed for AI agents to easily consume email content without handling raw MIME structures.

Dry Run Output:

{
  "thread_id": "thread-123",
  "message_id_header": "<123@example.com>",
  "references": "",
  "from": "sender@example.com",
  "reply_to": "",
  "to": "you@example.com",
  "cc": "",
  "subject": "Original subject",
  "date": "Thu, 1 Jan 2026 00:00:00 +0000",
  "body_text": "Original message body",
  "body_html": "<p>Original message body</p>"
}

Checklist:

  • My code follows the AGENTS.md guidelines (no generated google-* crates).
  • I have run cargo fmt --all to format the code perfectly.
  • I have run cargo clippy -- -D warnings and resolved all warnings.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have provided a Changeset file (e.g. via pnpx changeset) to document my changes.

@changeset-bot
Copy link

changeset-bot bot commented Mar 15, 2026

🦋 Changeset detected

Latest commit: 4db0c7b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@googleworkspace/cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@googleworkspace-bot googleworkspace-bot added area: distribution area: tui area: core Core CLI parsing, commands, error handling, utilities labels Mar 15, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the Gmail CLI with a new +read helper, streamlining the process of extracting clean text or HTML content from emails. This feature is particularly beneficial for AI agents, as it abstracts away the complexities of MIME parsing and base64 decoding. Concurrently, the PR includes a comprehensive update and optimization of project dependencies, which involved downgrading ratatui and removing a substantial number of unneeded crates, alongside setting a new minimum Rust version.

Highlights

  • New Gmail +read Helper: Introduced a new +read subcommand for the Gmail CLI, enabling simplified extraction of email body content and headers, specifically designed for AI agent consumption.
  • Dependency Optimization: Performed a significant cleanup and update of project dependencies, including adjusting the ratatui version and removing numerous unused packages from Cargo.lock.
  • CLI Subcommand Integration: Integrated the +read functionality into the main Gmail CLI, providing options for message ID, header inclusion, output format (text/json), HTML body preference, and dry-run capabilities.
Changelog
  • .changeset/feat-gmail-read.md
    • Added a new changeset file documenting the +read helper feature.
  • Cargo.lock
    • Updated and optimized various dependencies, including a downgrade of ratatui and removal of many unused packages.
    • Adjusted versions for syn, darling, itertools, time, and unicode-truncate.
    • Removed numerous packages like atomic, bit-set, bytemuck, csscolorparser, euclid, fancy-regex, filedescriptor, hex, kasuari, lab, line-clipping, mac_address, memmem, memoffset, minimal-lexical, nix, nom, num-derive, ordered-float, pest and its related crates, phf_codegen and its related crates, portable-atomic, ratatui-core and its related crates, regex, terminfo, termios, termwiz, ucd-trie, uuid, vtparse, and wezterm related crates.
  • Cargo.toml
    • Specified the minimum Rust version to 1.87.0.
    • Updated the ratatui dependency version from 0.30.0 to 0.29.0.
  • src/helpers/gmail/mod.rs
    • Introduced the read module and made the reply module private.
    • Added serde::Serialize import and derived Serialize for OriginalMessage.
    • Registered the new +read subcommand with its arguments and help text for the CLI.
  • src/helpers/gmail/read.rs
    • Added a new module containing the handle_read function.
    • Implemented logic for fetching, parsing, and outputting Gmail message content and headers based on user-specified formats and options.
  • src/setup_tui.rs
    • Imported the Stylize trait from ratatui::prelude.
Activity
  • dumko2001 opened this pull request to add a new Gmail +read helper.
  • dumko2001 implemented the core logic for extracting clean text from Gmail messages, handling MIME structures and base64 decoding.
  • dumko2001 updated and optimized project dependencies, including a significant cleanup of unused crates.
  • dumko2001 ensured all checklist items, including formatting, clippy warnings, and tests, were addressed.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Generative AI Prohibited Use Policy, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new +read helper for the gmail command, which is a useful addition for extracting email content. The implementation looks good. However, the PR also includes a significant and unexplained downgrade of the ratatui dependency from version 0.30.0 to 0.29.0. This change seems out of scope and should be addressed, either by providing a clear justification or by moving it to a separate pull request.

@dumko2001
Copy link
Author

Thanks for the catch! The ratatui downgrade was accidental (likely a rebase/merge artifact). I've restored it to 0.30.0 to match main. I also fixed an unused import warning in setup_tui.rs while I was at it.

@googleworkspace-bot
Copy link
Collaborator

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a +read helper for the Gmail CLI, which is a useful addition for extracting email content. The implementation is mostly solid, but I've found a couple of areas for improvement. There's a redundant CLI flag (--body-only) that could be removed to simplify the user interface. Additionally, the logic for handling HTML body content could be made more robust.

@googleworkspace-bot
Copy link
Collaborator

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a +read helper for the Gmail service, which is a useful addition for extracting email content. The implementation is clean and follows existing patterns. However, a high-severity robustness issue has been identified where the use of println! can lead to a panic if the output pipe is closed. A fix has been recommended to handle I/O errors gracefully, which will improve the robustness of the new helper.

Comment on lines +18 to +76
pub(super) async fn handle_read(
_doc: &crate::discovery::RestDescription,
matches: &ArgMatches,
) -> Result<(), GwsError> {
let message_id = matches
.get_one::<String>("id")
.unwrap();

let dry_run = matches.get_flag("dry-run");

let original = if dry_run {
OriginalMessage::dry_run_placeholder(message_id)
} else {
let t = auth::get_token(&[GMAIL_READONLY_SCOPE])
.await
.map_err(|e| GwsError::Auth(format!("Gmail auth failed: {e}")))?;

let client = crate::client::build_client()?;
fetch_message_metadata(&client, &t, message_id).await?
};

let format = matches.get_one::<String>("format").unwrap();
let show_headers = matches.get_flag("headers");
let use_html = matches.get_flag("html");

if format == "json" {
println!(
"{}",
serde_json::to_string_pretty(&original)
.map_err(|e| GwsError::Other(anyhow::anyhow!(e)))?
);
return Ok(());
}

if show_headers {
println!("From: {}", original.from);
println!("To: {}", original.to);
if !original.cc.is_empty() {
println!("Cc: {}", original.cc);
}
println!("Subject: {}", original.subject);
println!("Date: {}", original.date);
println!("---");
}

let body = if use_html {
original
.body_html
.as_deref()
.filter(|s| !s.is_empty())
.unwrap_or(&original.body_text)
} else {
&original.body_text
};

println!("{}", body);

Ok(())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This function uses println! multiple times to write to standard output. If the output is piped to another process that closes the pipe early (e.g., | head), println! will panic, causing the program to crash. This is a robustness issue that can affect consumers of this CLI, especially in scripted environments.

To prevent this, you should use writeln! on a handle to stdout and handle the potential io::Error, converting it to a GwsError.

For example:

use std::io::{self, Write};

let mut stdout = io::stdout();
writeln!(stdout, "From: {}", original.from).map_err(|e| GwsError::Other(anyhow::anyhow!(e)))?;

This should be applied to all println! calls in this function to make the helper more robust.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: core Core CLI parsing, commands, error handling, utilities area: tui

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants