Skip to content

feat: expose minimal library crate for programmatic API access#397

Open
zerone0x wants to merge 1 commit intogoogleworkspace:mainfrom
zerone0x:feat/lib-crate
Open

feat: expose minimal library crate for programmatic API access#397
zerone0x wants to merge 1 commit intogoogleworkspace:mainfrom
zerone0x:feat/lib-crate

Conversation

@zerone0x
Copy link
Contributor

Summary

  • Add src/lib.rs exposing six stable modules (discovery, error, config, services, validate, client) as a public library API
  • Extract config_dir() into a standalone src/config.rs module so it can be shared between library and binary without pulling in CLI-specific auth_commands
  • Add [lib] section to Cargo.toml alongside the existing [[bin]] so the crate builds both a library and a binary
  • Update main.rs to import shared modules from the library crate via use gws::*

This follows the design principles from #386: minimal surface area, no CLI coupling (library consumers don't need clap, ratatui, or interactive OAuth), and facade over internals. Keeps a deliberately smaller API surface than PR #376.

Modules exposed (pub mod)

Module Purpose
discovery RestDescription, RestMethod, RestResource — introspect Google APIs
error GwsError — unified error type
config config_dir() — resolve the gws configuration directory
services resolve_service(), SERVICES — service name to API mapping
validate validate_api_identifier(), input safety helpers
client build_client(), send_with_retry() — HTTP with retry

Modules kept internal

auth, auth_commands, executor, helpers/*, formatter, setup, setup_tui, credential_store — all CLI-specific.

Fixes #386

Test plan

  • cargo check --lib — library compiles independently
  • cargo check — full binary + library builds
  • cargo test — all 502 existing tests pass
  • cargo clippy -- -D warnings — clean

🤖 Generated with Claude Code

Add a lib.rs with focused public API surface exposing discovery, error,
config, services, validate, and client modules. This enables gws to be
used as a Rust library dependency for CI integrations and custom
automation without pulling in CLI-specific dependencies (clap, ratatui,
interactive OAuth).

- Create src/lib.rs with pub mod for the six stable modules
- Add [lib] section to Cargo.toml alongside existing [[bin]]
- Extract config_dir() into standalone src/config.rs module
- Update main.rs to import shared modules from the library crate
- Update discovery.rs to use config::config_dir() instead of
  auth_commands::config_dir()

Keeps a smaller surface area than PR googleworkspace#376 by only exposing modules that
are stable and useful outside the CLI, with no CLI coupling.

Fixes googleworkspace#386

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@zerone0x zerone0x requested a review from jpoehnelt as a code owner March 11, 2026 08:37
@changeset-bot
Copy link

changeset-bot bot commented Mar 11, 2026

⚠️ No Changeset found

Latest commit: 5a311cd

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@googleworkspace-bot googleworkspace-bot added area: auth area: distribution area: discovery area: core Core CLI parsing, commands, error handling, utilities labels Mar 11, 2026
@google-cla
Copy link

google-cla bot commented Mar 11, 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 refactors the project structure to introduce a dedicated library crate, enabling programmatic access to Google Workspace APIs without the overhead of CLI-specific dependencies. This change enhances modularity and reusability, allowing external applications to integrate with the core functionalities more cleanly, while maintaining a minimal and focused API surface.

Highlights

  • New Library Crate: A new library crate (src/lib.rs) was introduced, exposing six stable modules (discovery, error, config, services, validate, client) for programmatic API access, adhering to a minimal API surface.
  • Configuration Directory Extraction: The config_dir() function was extracted from src/auth_commands.rs into a new standalone src/config.rs module, allowing it to be shared between the library and binary without pulling in CLI-specific dependencies.
  • Cargo.toml Update: The Cargo.toml file was updated to include a [lib] section, enabling the project to build both a library and an executable binary from a single package.
  • Main Binary Refactoring: The main.rs file was refactored to import shared modules from the new gws library crate via use gws::*, reducing coupling and promoting code reuse within the main application.
Changelog
  • Cargo.toml
    • Added [lib] section to define the gws library target.
  • src/auth_commands.rs
    • Replaced the inline config_dir implementation with a call to crate::config::config_dir().
  • src/config.rs
    • Added a new file containing the config_dir function, previously in src/auth_commands.rs.
  • src/discovery.rs
    • Updated the cache_dir resolution to use crate::config::config_dir() instead of crate::auth_commands::config_dir().
  • src/lib.rs
    • Added a new file to serve as the entry point for the gws library crate.
    • Declared public modules: client, config, discovery, error, services, validate.
  • src/main.rs
    • Removed direct mod declarations for client, discovery, error, services, and validate.
    • Added use gws::* statements to import these modules from the new library crate.
Activity
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 project to expose a public library crate, which is a great improvement for reusability. The changes are well-structured. I've found a potential security and correctness issue in the new config_dir function where it could fall back to using the current working directory, potentially writing sensitive configuration to an insecure location. I've suggested changing it to return a Result to make the API more robust and prevent silent failures.

//! the `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` environment variable and falling back
//! to `~/.config/gws` (or the OS-specific legacy path for existing installs).

use std::path::PathBuf;
Copy link
Contributor

Choose a reason for hiding this comment

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

high

To support returning a Result from config_dir and provide more specific error information, GwsError should be imported.

Suggested change
use std::path::PathBuf;
use crate::error::GwsError;
use std::path::PathBuf;

Comment on lines +30 to +54
pub fn config_dir() -> PathBuf {
if let Ok(dir) = std::env::var("GOOGLE_WORKSPACE_CLI_CONFIG_DIR") {
return PathBuf::from(dir);
}

// Use ~/.config/gws on all platforms for a consistent, user-friendly path.
let primary = dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".config")
.join("gws");
if primary.exists() {
return primary;
}

// Backward compat: fall back to OS-specific config dir for existing installs
// (e.g. ~/Library/Application Support/gws on macOS, %APPDATA%\gws on Windows).
let legacy = dirs::config_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("gws");
if legacy.exists() {
return legacy;
}

primary
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The function config_dir falls back to the current working directory (.) if the user's home or config directory cannot be found. This can lead to configuration files containing sensitive credentials being created in unexpected locations, such as a version-controlled directory or a world-readable temporary folder.

For a robust library API, it's better to return a Result and let the caller decide how to handle the error, rather than failing silently in a potentially insecure way. This change will require updating the call sites to handle the Result.

pub fn config_dir() -> Result<PathBuf, GwsError> {
    if let Ok(dir) = std::env::var("GOOGLE_WORKSPACE_CLI_CONFIG_DIR") {
        return Ok(PathBuf::from(dir));
    }

    let home_dir = dirs::home_dir().ok_or_else(|| {
        GwsError::Validation(
            "Could not determine home directory. Ensure $HOME is set.".to_string(),
        )
    })?;

    // Use ~/.config/gws on all platforms for a consistent, user-friendly path.
    let primary = home_dir.join(".config").join("gws");
    if primary.exists() {
        return Ok(primary);
    }

    // Backward compat: fall back to OS-specific config dir for existing installs
    // (e.g. ~/Library/Application Support/gws on macOS, %APPDATA%\gws on Windows).
    if let Some(config_dir) = dirs::config_dir() {
        let legacy = config_dir.join("gws");
        if legacy.exists() {
            return Ok(legacy);
        }
    }

    Ok(primary)
}

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

Labels

area: auth area: core Core CLI parsing, commands, error handling, utilities area: discovery area: distribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: expose minimal library crate for programmatic API access

2 participants