Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/extract-library-crate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"@googleworkspace/cli": minor
---

Extract `google-workspace` library crate for programmatic Rust API access (closes #386)

Introduces a Cargo workspace with a new `google-workspace` library crate (`crates/google-workspace/`)
that exposes the core modules for use as a Rust dependency:

- `discovery` — Discovery Document types and fetching
- `error` — Structured `GwsError` type
- `services` — Service registry and resolution
- `validate` — Input validation and URL encoding
- `client` — HTTP client with retry logic

The `gws` binary crate re-exports all library types transparently — zero behavioral changes.
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:
rust:
- '**/*.rs'
- 'Cargo.toml'
- 'crates/*/Cargo.toml'
- 'Cargo.lock'
- 'build.rs'
- '.cargo/**'
Expand Down Expand Up @@ -87,7 +88,7 @@ jobs:
key: test-${{ matrix.os }}

- name: Run tests
run: cargo test --verbose
run: cargo test --workspace --verbose

nix:
name: Nix
Expand Down Expand Up @@ -146,7 +147,7 @@ jobs:
fi

- name: Clippy
run: cargo clippy -- -D warnings
run: cargo clippy --workspace -- -D warnings


skills:
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/policy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ jobs:
fetch-depth: 0
- name: Enforce AGENTS.md rules
run: |
if grep -qE "^google-[a-zA-Z0-9_-]+[[:space:]]*=" Cargo.toml; then
echo "::error file=Cargo.toml::Violates AGENTS.md: Adding generated google-* crates is prohibited. The CLI uses dynamic schema discovery at runtime."
# Check CLI crate for prohibited google-* registry crates.
# Path dependencies (e.g. google-workspace = { path = ... }) are allowed.
if grep -E "^google-[a-zA-Z0-9_-]+[[:space:]]*=" crates/google-workspace-cli/Cargo.toml | grep -v 'path[[:space:]]*='; then
echo "::error file=crates/google-workspace-cli/Cargo.toml::Violates AGENTS.md: Adding generated google-* crates is prohibited. The CLI uses dynamic schema discovery at runtime."
exit 1
fi
echo "Policy check passed."
Expand Down
19 changes: 18 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 3 additions & 68 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,76 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.

[package]
name = "gws"
version = "0.20.1"
edition = "2021"
description = "Google Workspace CLI — dynamic command surface from Discovery Service"
license = "Apache-2.0"
repository = "https://github.com/googleworkspace/cli"
homepage = "https://github.com/googleworkspace/cli"
readme = "README.md"
authors = ["Justin Poehnelt"]
keywords = ["cli", "google-workspace", "google", "drive", "gmail"]
categories = ["command-line-utilities", "web-programming"]

[[bin]]
name = "gws"
path = "src/main.rs"



[dependencies]
tempfile = "3"
aes-gcm = "0.10"
anyhow = "1"
clap = { version = "4", features = ["derive", "string"] }
dirs = "5"
dotenvy = "0.15"
hostname = "0.4"
reqwest = { version = "0.12", features = ["json", "stream", "rustls-tls-native-roots"], default-features = false }
rand = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.10"
thiserror = "2"
tokio = { version = "1", features = ["full"] }
yup-oauth2 = "12"
futures-util = "0.3"
tokio-util = { version = "0.7", features = ["io"] }
bytes = "1"
base64 = "0.22.1"
derive_builder = "0.20.2"
ratatui = "0.30.0"
crossterm = "0.29.0"
chrono = "0.4.44"
chrono-tz = "0.10"
iana-time-zone = "0.1"
mail-builder = "0.4"
async-trait = "0.1.89"
serde_yaml = "0.9.34"
percent-encoding = "2.3.2"
zeroize = { version = "1.8.2", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
tracing-appender = "0.2"
uuid = { version = "1.22.0", features = ["v4", "v5"] }
mime_guess2 = "2.3.1"

[target.'cfg(target_os = "macos")'.dependencies]
keyring = { version = "3.6.3", features = ["apple-native"] }

[target.'cfg(target_os = "windows")'.dependencies]
keyring = { version = "3.6.3", features = ["windows-native"] }

[target.'cfg(not(any(target_os = "macos", target_os = "windows")))'.dependencies]
keyring = "3.6.3"

[workspace]
members = ["crates/google-workspace-cli", "crates/google-workspace"]
resolver = "2"

# The profile that 'cargo dist' will build with
[profile.dist]
inherits = "release"
lto = "thin"

[dev-dependencies]
serial_test = "3.4.0"
80 changes: 80 additions & 0 deletions crates/google-workspace-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# 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.

[package]
name = "google-workspace-cli"
version = "0.20.1"
edition = "2021"
description = "Google Workspace CLI — dynamic command surface from Discovery Service"
license = "Apache-2.0"
repository = "https://github.com/googleworkspace/cli"
homepage = "https://github.com/googleworkspace/cli"
readme = "../../README.md"
authors = ["Justin Poehnelt"]
keywords = ["cli", "google-workspace", "google", "drive", "gmail"]
categories = ["command-line-utilities", "web-programming"]

[[bin]]
name = "gws"
path = "src/main.rs"

[dependencies]
google-workspace = { path = "../google-workspace" }
tempfile = "3"
aes-gcm = "0.10"
anyhow = "1"
clap = { version = "4", features = ["derive", "string"] }
dirs = "5"
dotenvy = "0.15"
hostname = "0.4"
reqwest = { version = "0.12", features = ["json", "stream", "rustls-tls-native-roots"], default-features = false }
rand = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.10"
thiserror = "2"
tokio = { version = "1", features = ["full"] }
yup-oauth2 = "12"
futures-util = "0.3"
tokio-util = { version = "0.7", features = ["io"] }
bytes = "1"
base64 = "0.22.1"
derive_builder = "0.20.2"
ratatui = "0.30.0"
crossterm = "0.29.0"
chrono = "0.4.44"
chrono-tz = "0.10"
iana-time-zone = "0.1"
mail-builder = "0.4"
async-trait = "0.1.89"
serde_yaml = "0.9.34"
percent-encoding = "2.3.2"
zeroize = { version = "1.8.2", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
tracing-appender = "0.2"
uuid = { version = "1.22.0", features = ["v4", "v5"] }
mime_guess2 = "2.3.1"

[target.'cfg(target_os = "macos")'.dependencies]
keyring = { version = "3.6.3", features = ["apple-native"] }

[target.'cfg(target_os = "windows")'.dependencies]
keyring = { version = "3.6.3", features = ["windows-native"] }

[target.'cfg(not(any(target_os = "macos", target_os = "windows")))'.dependencies]
keyring = "3.6.3"

[dev-dependencies]
serial_test = "3.4.0"
File renamed without changes.
File renamed without changes.
17 changes: 17 additions & 0 deletions crates/google-workspace-cli/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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.

//! HTTP client — re-exports from `google_workspace` library crate.

pub use google_workspace::client::*;
File renamed without changes.
File renamed without changes.
33 changes: 33 additions & 0 deletions crates/google-workspace-cli/src/discovery.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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.

//! Discovery Document types and fetching.
//!
//! Types are re-exported from the `google_workspace` library crate.
//! The CLI wrapper provides default caching via `config_dir()`.

pub use google_workspace::discovery::*;

/// Fetches and caches a Google Discovery Document using the CLI's config directory.
///
/// This is a convenience wrapper around
/// [`google_workspace::discovery::fetch_discovery_document`] that automatically
/// uses the CLI's cache directory (`~/.config/gws/cache/`).
pub async fn fetch_discovery_document(
service: &str,
version: &str,
) -> anyhow::Result<RestDescription> {
let cache_dir = crate::auth_commands::config_dir().join("cache");
google_workspace::discovery::fetch_discovery_document(service, version, Some(&cache_dir)).await
}
Loading
Loading