feat: expose minimal library crate for programmatic API access#397
feat: expose minimal library crate for programmatic API access#397zerone0x wants to merge 1 commit intogoogleworkspace:mainfrom
Conversation
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>
|
|
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. |
Summary of ChangesHello, 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
Changelog
Activity
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
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; |
| 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 | ||
| } |
There was a problem hiding this comment.
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)
}
Summary
src/lib.rsexposing six stable modules (discovery,error,config,services,validate,client) as a public library APIconfig_dir()into a standalonesrc/config.rsmodule so it can be shared between library and binary without pulling in CLI-specificauth_commands[lib]section toCargo.tomlalongside the existing[[bin]]so the crate builds both a library and a binarymain.rsto import shared modules from the library crate viause 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)
discoveryRestDescription,RestMethod,RestResource— introspect Google APIserrorGwsError— unified error typeconfigconfig_dir()— resolve the gws configuration directoryservicesresolve_service(),SERVICES— service name to API mappingvalidatevalidate_api_identifier(), input safety helpersclientbuild_client(),send_with_retry()— HTTP with retryModules 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 independentlycargo check— full binary + library buildscargo test— all 502 existing tests passcargo clippy -- -D warnings— clean🤖 Generated with Claude Code