From 5a311cdb09f06dc9c68a6c1cea973024250bc869 Mon Sep 17 00:00:00 2001 From: zerone0x Date: Wed, 11 Mar 2026 16:37:32 +0800 Subject: [PATCH] feat: expose minimal library crate for programmatic API access 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 #376 by only exposing modules that are stable and useful outside the CLI, with no CLI coupling. Fixes #386 Co-Authored-By: Claude Opus 4.6 --- Cargo.toml | 4 ++++ src/auth_commands.rs | 24 +------------------- src/config.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++ src/discovery.rs | 2 +- src/lib.rs | 39 ++++++++++++++++++++++++++++++++ src/main.rs | 13 +++++++---- 6 files changed, 107 insertions(+), 29 deletions(-) create mode 100644 src/config.rs create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 44bf0235..45f8accc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,10 @@ authors = ["Justin Poehnelt"] keywords = ["cli", "google-workspace", "google", "drive", "gmail"] categories = ["command-line-utilities", "web-programming"] +[lib] +name = "gws" +path = "src/lib.rs" + [[bin]] name = "gws" path = "src/main.rs" diff --git a/src/auth_commands.rs b/src/auth_commands.rs index ade681d6..a2899aba 100644 --- a/src/auth_commands.rs +++ b/src/auth_commands.rs @@ -92,29 +92,7 @@ const READONLY_SCOPES: &[&str] = &[ ]; 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 + crate::config::config_dir() } fn plain_credentials_path() -> PathBuf { diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 00000000..f8d436a3 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,54 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Configuration directory resolution. +//! +//! Provides [`config_dir()`] to locate the gws config directory, respecting +//! 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; + +/// Returns the gws configuration directory. +/// +/// Resolution order: +/// 1. `GOOGLE_WORKSPACE_CLI_CONFIG_DIR` environment variable (if set) +/// 2. `~/.config/gws` (if it exists — primary location) +/// 3. OS-specific config dir / `gws` (legacy fallback for existing installs) +/// 4. `~/.config/gws` (default for new installs) +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 +} diff --git a/src/discovery.rs b/src/discovery.rs index b4fa9ff1..5a6bad5c 100644 --- a/src/discovery.rs +++ b/src/discovery.rs @@ -195,7 +195,7 @@ pub async fn fetch_discovery_document( let version = crate::validate::validate_api_identifier(version).map_err(|e| anyhow::anyhow!("{e}"))?; - let cache_dir = crate::auth_commands::config_dir().join("cache"); + let cache_dir = crate::config::config_dir().join("cache"); std::fs::create_dir_all(&cache_dir)?; let cache_file = cache_dir.join(format!("{service}_{version}.json")); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..b8f9f323 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,39 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Google Workspace CLI — library crate. +//! +//! This crate exposes a focused subset of `gws` internals for programmatic +//! access to Google Workspace APIs. Library consumers can introspect API +//! schemas, resolve service names, validate inputs, and build authenticated +//! HTTP clients without depending on any CLI-specific code (clap, ratatui, +//! interactive OAuth flows, etc.). +//! +//! # Exposed modules +//! +//! | 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 | + +pub mod client; +pub mod config; +pub mod discovery; +pub mod error; +pub mod services; +pub mod validate; diff --git a/src/main.rs b/src/main.rs index 22259a44..c671811a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,11 +21,8 @@ mod auth; pub(crate) mod auth_commands; -mod client; mod commands; pub(crate) mod credential_store; -mod discovery; -mod error; mod executor; mod formatter; mod fs_util; @@ -33,12 +30,18 @@ mod generate_skills; mod helpers; mod oauth_config; mod schema; -mod services; mod setup; mod setup_tui; mod text; mod token_storage; -pub(crate) mod validate; + +// Re-use modules from the library crate +use gws::client; +use gws::config; +use gws::discovery; +use gws::error; +use gws::services; +use gws::validate; use error::{print_error_json, GwsError};