Skip to content

feat: expose library crate for programmatic API access#214

Open
geofflittle wants to merge 1 commit intogoogleworkspace:mainfrom
geofflittle:feat/library-crate
Open

feat: expose library crate for programmatic API access#214
geofflittle wants to merge 1 commit intogoogleworkspace:mainfrom
geofflittle:feat/library-crate

Conversation

@geofflittle
Copy link

Description

Adds a library crate (lib.rs) so that gws can be used as a Rust dependency for programmatic access to Google Workspace APIs — without shelling out to the binary.

Addresses #211.

Two coupling points previously prevented a clean lib/bin split:

  1. config_dir() lived in the CLI-only auth_commands module but was called by 6 library modules (accounts, credential_store, oauth_config, auth, discovery)
  2. Model Armor sanitization types (SanitizeMode, SanitizeConfig, SanitizationResult, sanitize_text()) lived in helpers/modelarmor but were used by executor

This PR extracts both into standalone modules (config.rs, sanitize.rs), creates lib.rs re-exporting library-worthy modules, and adds a [lib] section to Cargo.toml. The original call sites delegate to the new modules, so all existing code paths are unchanged.

Also moves parse_service_and_version() from main.rs to services.rs so it is accessible from both crate roots.

Zero behavior changes to the binary. All existing tests pass.

Dry Run Output:

N/A — structural refactor, no new CLI commands or API requests

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.

Notes

  • cargo clippy -- -D warnings is clean for all new/modified code. There is 1 pre-existing should_implement_trait warning in formatter.rs:55 (unrelated to this PR).
  • New integration tests (tests/lib_integration.rs) verify the library surface: RestDescription deserialization, resolve_service(), GwsError, validate_api_identifier().
  • The #![allow(dead_code)] on lib.rs suppresses warnings for pub(crate) modules that are only used by the binary target (dual compilation — same .rs files compiled for both targets).

@geofflittle geofflittle requested a review from jpoehnelt as a code owner March 5, 2026 22:15
@changeset-bot
Copy link

changeset-bot bot commented Mar 5, 2026

🦋 Changeset detected

Latest commit: e90ae20

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

@google-cla
Copy link

google-cla bot commented Mar 5, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@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 significantly enhances the gws project by introducing a dedicated library crate. This change allows developers to integrate gws's core functionalities directly into their Rust applications, moving beyond its previous CLI-only usage. The refactoring involved extracting shared utilities like configuration directory resolution and Model Armor sanitization into independent modules, thereby reducing coupling and improving the overall modularity and reusability of the codebase.

Highlights

  • Library Crate Exposure: A new library crate (lib.rs) has been added to allow programmatic access to the gws functionality, enabling its use as a Rust dependency without relying on the CLI binary.
  • Code Refactoring for Modularity: The config_dir() function, previously coupled with CLI-only auth_commands, has been extracted into a new standalone src/config.rs module. Similarly, Model Armor sanitization types (SanitizeMode, SanitizeConfig, SanitizationResult, sanitize_text()) were moved from helpers/modelarmor to a new src/sanitize.rs module.
  • Improved Service Parsing: The parse_service_and_version() function has been relocated from main.rs to services.rs, making it accessible to both the binary and the new library crate.
  • No Behavioral Changes: This refactor introduces no changes to the existing binary's behavior, ensuring backward compatibility. All existing tests continue to pass.
  • New Integration Tests: New integration tests (tests/lib_integration.rs) have been added to specifically verify the exposed library surface, including RestDescription deserialization, resolve_service(), GwsError, and validate_api_identifier().
Changelog
  • .changeset/add-library-crate.md
    • Added a new changeset file documenting the addition of the library crate and the associated refactoring.
  • Cargo.toml
    • Added a [lib] section to define the new gws library crate, pointing to src/lib.rs.
  • src/accounts.rs
    • Updated the call to config_dir() to reference the newly created crate::config module.
  • src/auth.rs
    • Updated the call to config_dir() to reference the newly created crate::config module.
  • src/auth_commands.rs
    • Removed the implementation of config_dir(), now delegating its call to crate::config::config_dir().
  • src/config.rs
    • Added a new module containing the config_dir() function, centralizing configuration directory logic.
  • src/credential_store.rs
    • Updated all calls to config_dir() to reference the newly created crate::config module.
  • src/discovery.rs
    • Updated the call to config_dir() to reference the newly created crate::config module.
  • src/executor.rs
    • Updated references to Model Armor sanitization types and functions to use the new crate::sanitize module.
  • src/helpers/modelarmor.rs
    • Removed the original definitions of Model Armor sanitization types and functions, now re-exporting them from crate::sanitize.
  • src/lib.rs
    • Added a new library crate file, re-exporting public modules to expose the programmatic API.
  • src/main.rs
    • Imported the new config and sanitize modules and updated the call to parse_service_and_version to use the services module.
  • src/mcp_server.rs
    • Updated all calls to parse_service_and_version to use the services module.
  • src/oauth_config.rs
    • Updated the call to config_dir() to reference the newly created crate::config module.
  • src/sanitize.rs
    • Added a new module containing Model Armor sanitization types and functions, previously in helpers/modelarmor.rs.
  • src/services.rs
    • Moved the parse_service_and_version() function from main.rs to this module.
  • tests/lib_integration.rs
    • Added new integration tests to verify the functionality exposed by the library crate.
Activity
  • The pull request author confirmed that the code adheres to AGENTS.md guidelines, cargo fmt --all was run, and cargo clippy -- -D warnings passed (with one pre-existing warning noted).
  • Tests were added to prove the feature works, specifically new integration tests for the library surface.
  • A Changeset file was provided to document the changes.
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 effectively refactors the codebase to expose a library crate, which is a great step towards making gws more reusable. The extraction of configuration and sanitization logic into their own modules is clean and well-executed. I have one suggestion to make the new library API for SanitizeMode more idiomatic.

@geofflittle
Copy link
Author

I have signed the CLA — https://cla.developers.google.com/ shows it under g.little712@gmail.com / geofflittle.

@geofflittle
Copy link
Author

Removed the Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> trailer that was failing the CLA check — that email can't sign a CLA. Force-pushed with a clean commit.

Extract `config_dir()` into `src/config.rs` and Model Armor sanitization
types into `src/sanitize.rs` so they can be shared between the binary and
library targets without pulling in CLI-only code. Add `src/lib.rs` with
public module re-exports and `tests/lib_integration.rs` with offline tests.

Also moves `parse_service_and_version()` from `main.rs` to `services.rs`
so it is accessible from both the lib and bin crate roots.

Zero behavior changes to the binary.
@googleworkspace-bot googleworkspace-bot added area: distribution area: core Core CLI parsing, commands, error handling, utilities labels Mar 5, 2026
@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 effectively refactors the codebase to expose a library crate, enabling programmatic use of gws. The separation of concerns is well-executed by extracting shared logic into new modules like config.rs and sanitize.rs. The new library structure in lib.rs and the corresponding Cargo.toml changes are correct. I've identified one critical security vulnerability in the new sanitize.rs module related to input validation, which I've detailed in a specific comment.

Comment on lines +134 to +143
pub fn build_sanitize_request_data(
template: &str,
text: &str,
method: &str,
) -> Result<(String, String), GwsError> {
let location = extract_location(template).ok_or_else(|| {
GwsError::Validation(
"Cannot extract location from --sanitize template. Expected format: projects/PROJECT/locations/LOCATION/templates/TEMPLATE".to_string(),
)
})?;
Copy link
Contributor

Choose a reason for hiding this comment

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

security-critical critical

The template parameter, which can be user-controlled, is used to construct a request URL without validation. The location extracted from it is used to form a hostname, and the template string itself is part of the URL path. This could lead to a Server-Side Request Forgery (SSRF) vulnerability if a malicious value is provided. I recommend validating both the template and the extracted location using the existing validation utilities to prevent this.

Suggested change
pub fn build_sanitize_request_data(
template: &str,
text: &str,
method: &str,
) -> Result<(String, String), GwsError> {
let location = extract_location(template).ok_or_else(|| {
GwsError::Validation(
"Cannot extract location from --sanitize template. Expected format: projects/PROJECT/locations/LOCATION/templates/TEMPLATE".to_string(),
)
})?;
pub fn build_sanitize_request_data(
template: &str,
text: &str,
method: &str,
) -> Result<(String, String), GwsError> {
let template = crate::validate::validate_resource_name(template)?;
let location = extract_location(template).ok_or_else(|| {
GwsError::Validation(
"Cannot extract location from --sanitize template. Expected format: projects/PROJECT/locations/LOCATION/templates/TEMPLATE".to_string(),
)
})?;
let location = crate::validate::validate_api_identifier(location)?;

@codecov
Copy link

codecov bot commented Mar 6, 2026

Codecov Report

❌ Patch coverage is 55.40541% with 66 lines in your changes missing coverage. Please review.
✅ Project coverage is 57.58%. Comparing base (132c3b1) to head (e90ae20).
⚠️ Report is 14 commits behind head on main.

Files with missing lines Patch % Lines
src/sanitize.rs 58.97% 32 Missing ⚠️
src/services.rs 0.00% 20 Missing ⚠️
src/mcp_server.rs 0.00% 6 Missing ⚠️
src/executor.rs 50.00% 3 Missing ⚠️
src/main.rs 0.00% 2 Missing ⚠️
src/auth.rs 0.00% 1 Missing ⚠️
src/config.rs 94.73% 1 Missing ⚠️
src/discovery.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #214      +/-   ##
==========================================
+ Coverage   57.52%   57.58%   +0.05%     
==========================================
  Files          38       40       +2     
  Lines       14188    14209      +21     
==========================================
+ Hits         8162     8182      +20     
- Misses       6026     6027       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants