gws-builder
Build-time Rust code generation from Google API Discovery documents: typed structs, enums, method parameter helpers, and static ActionDescriptor data for agents. The default HttpFetcher uses blocking HTTP (ureq), suitable for build.rs.
Design details and roadmap are in docs/BUILD_PLAN.md.
Whitelist-driven generation (recommended)
Only what you configure is generated:
BuilderConfig::services— list only the Google APIs you need (e.g.drive+gmail), each with a version (v3,v1, …). APIs not listed are not emitted at all.- Per-API whitelist — use [
ServiceSpec::whitelist] so each service includes only the REST methods matching your patterns. Unused methods and unreachable schemas are dropped (smaller crates, fewer OAuth scopes to reason about).
Discover method ids first, then encode them as patterns (files.*, users.messages.list, users.**):
use ;
let actions = list_available_actions?;
for a in &actions
Empty whitelist patterns are rejected at generate() time.
Add to your crate
In the consumer crate that will run codegen (often the same crate that owns build.rs):
[]
= { = "../gws-builder" } # or version from crates.io when published
The generated crate (the src/ that includes generated/gws_types/mod.rs) also needs these runtime dependencies so emitted types and serde_helpers.rs compile:
[]
= { = "1", = ["derive"] }
= "1"
= "0.22"
(base64 is used for Google format: byte fields deserialized via serde_helpers::deserialize_bytes_base64.)
Minimal build.rs (whitelist)
use PathBuf;
use ;
ActionFilter::All still exists for bring-up or tooling, but downstream products should prefer whitelists so generated code matches the surface you actually ship.
Generation runs when you build; cargo build must be able to reach https://www.googleapis.com (or use a cache—see below).
Use the generated code
Point a module at the generated mod.rs (Pattern B: checked into the repo, one directory per out_dir):
// In src/lib.rs or src/main.rs of the consumer crate:
use File;
use all_actions;
Adjust the #[path = ...] relative to the file that contains it.
API keys, OAuth, and “which Workspace user?”
Handle this in the downstream consumer, not in gws-builder.
gws-builder only downloads public Discovery documents and emits Rust types plus static ActionDescriptor metadata. It does not call Google APIs on behalf of an account, and it has no notion of API keys, OAuth clients, refresh tokens, or a target user or domain. Adding those concerns here would mix build-time codegen with runtime credentials and would encourage secrets in the wrong place.
What the consumer owns:
| Concern | Where it lives |
|---|---|
| Google Cloud project, OAuth client ID/secret (or service account JSON) | Consumer app config / secret store (env, vault), never in generated code |
| Which human or mailbox “this run” acts as | The identity behind OAuth (user consent) or domain-wide delegation (service account impersonating user@example.com, configured by a Workspace admin) |
Access tokens, refresh tokens, Authorization: Bearer … |
Your HTTP client at runtime (reqwest, hyper, etc.) |
| Choosing scopes | Match ActionDescriptor::scopes (and Google’s docs) to what you request at consent time |
API keys alone are usually not enough for private Workspace data (Drive files, Gmail, Calendar events). Google expects OAuth 2.0 (user or delegated) or service accounts with appropriate Workspace admin setup. Your agent crate should use the official patterns from Google’s OAuth documentation.
Example: realistic downstream layout
Imagine an internal agent binary workspace-agent that lists Drive files for the signed-in user:
- Build time —
build.rsrunsgws_builder::generate(as above) and checks ingenerated/gws_types/. - Runtime —
mainloads client credentials from the environment, runs an OAuth flow (or reads a stored refresh token), obtains an access token, and calls REST endpoints that match the generated types.
workspace-agent/
build.rs # gws-builder only: types + ActionDescriptors
src/
main.rs # OAuth + HTTP; uses generated modules
generated/gws_types/ # committed (Pattern B) or generated in CI
// Pseudocode — illustrative only; use a real OAuth crate and token storage.
// After codegen: mod gws_types { ... }
use drive; // generated structs + LIST_ACTION, etc.
async
The generated ActionDescriptor values (scopes, path templates, HTTP methods) are there so your consumer can drive consent prompts, tool routing, and request construction — not so gws-builder can store tenants or keys.
Output layout
{out_dir}/— e.g.generated/gws_types/:mod.rs,serde_helpers.rs, one*.rsper API (e.g.drive.rs).generation_manifest.json— written next to the parent ofout_dir(e.g.generated/generation_manifest.json), used forRegenerationPolicy::IfChanged/IfMissing.
Action filters
ActionFilter controls which REST methods are kept (and which schemas survive pruning):
All— every method.Whitelist(vec)— patterns such as:files.list— resourcefiles, methodlistfiles.*— all methods on resourcefilesusers.messages.*— all methods on nested resourceusers.messagesusers.**— all methods onusersand any sub-resource under it
Blacklist(vec)— same pattern syntax, methods excluded.
Example:
filter: Whitelist,
Regeneration policy
IfChanged(default) — fetch Discovery JSON; if revision, checksum, and filter fingerprint match the manifest and the service file exists, skip that service.Always— always fetch and regenerate.IfMissing— skip when the manifest already lists the service and the service.rsfile exists.Never— do not fetch; require existinggeneration_manifest.jsonand the service.rsfile (fail if missing).
Listing actions before you whitelist
Use this from a small binary or a one-off build.rs helper to print available methods (no code generation):
use ;
let actions = list_available_actions?;
for a in &actions
Caching Discovery JSON
If the network fetch fails, you can set cache_dir on BuilderConfig to a directory where gws-builder will write {service}_{version}.json after a successful fetch and read it back on failure.
Tests and development
This repository’s crate:
To smoke-test codegen into ./generated/gws_types (embedded sample Discovery JSON, no network):
To fetch Drive v3, Gmail v1, and Calendar v3 with full surface (ActionFilter::All, for exploration or baselines):
For a consumer-style run (only the APIs and method patterns you list — recommended for real apps):
That output path is listed in .gitignore so generated files stay local unless you force-add them.