|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +## Project overview |
| 4 | + |
| 5 | +smithy-go is the Go code generator and runtime for [Smithy](https://smithy.io/). |
| 6 | +It has two major components: |
| 7 | + |
| 8 | +1. **Codegen** (`codegen/`) — A Smithy build plugin written in Java that |
| 9 | + generates Go client/server/shape code from Smithy models. |
| 10 | +2. **Runtime** (`./`, top-level Go module) — The Go packages that generated |
| 11 | + code depends on at runtime. |
| 12 | + |
| 13 | +The primary downstream consumer is |
| 14 | +[aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2). |
| 15 | + |
| 16 | +## Repository layout |
| 17 | + |
| 18 | +``` |
| 19 | +. # Root Go module (github.com/aws/smithy-go) |
| 20 | +├── auth/ # Auth identity + scheme interfaces |
| 21 | +│ └── bearer/ # Bearer token auth |
| 22 | +├── aws-http-auth/ # Separate module: AWS SigV4/SigV4A HTTP signing |
| 23 | +├── codegen/ # Java/Gradle: Smithy code generator |
| 24 | +│ ├── smithy-go-codegen/ # Main codegen source (Java) |
| 25 | +│ └── smithy-go-codegen-test/ # Codegen integration tests |
| 26 | +├── container/ # Generic container types |
| 27 | +├── context/ # Context helpers |
| 28 | +├── document/ # Smithy document type abstraction |
| 29 | +│ └── json/ # JSON document codec |
| 30 | +├── encoding/ # Wire format encoders/decoders |
| 31 | +│ ├── cbor/ # CBOR (used by rpcv2Cbor) |
| 32 | +│ ├── httpbinding/ # HTTP binding serde helpers |
| 33 | +│ ├── json/ # JSON encoder/decoder |
| 34 | +│ └── xml/ # XML encoder/decoder |
| 35 | +├── endpoints/ # Endpoint resolution types |
| 36 | +├── internal/ # Internal utilities (singleflight, etc.) |
| 37 | +├── io/ # I/O helpers |
| 38 | +├── logging/ # Logging interfaces |
| 39 | +├── metrics/ # Metrics interfaces |
| 40 | +│ └── smithyotelmetrics/ # Separate module: OpenTelemetry metrics adapter |
| 41 | +├── middleware/ # Middleware stack (the core of the operation pipeline) |
| 42 | +├── ptr/ # Pointer-to/from-value helpers |
| 43 | +├── testing/ # Test assertion helpers for generated protocol tests |
| 44 | +│ └── xml/ # XML comparison utilities |
| 45 | +├── time/ # Smithy timestamp format helpers |
| 46 | +├── tracing/ # Tracing interfaces |
| 47 | +│ └── smithyoteltracing/ # Separate module: OpenTelemetry tracing adapter |
| 48 | +└── transport/ |
| 49 | + └── http/ # HTTP request/response types and middleware |
| 50 | +``` |
| 51 | + |
| 52 | +## Building and testing |
| 53 | + |
| 54 | +### Runtime (Go) |
| 55 | + |
| 56 | +```bash |
| 57 | +# Run unit tests |
| 58 | +make unit |
| 59 | +``` |
| 60 | + |
| 61 | +### Codegen (Java) |
| 62 | + |
| 63 | +```bash |
| 64 | +# Build and test codegen |
| 65 | +cd codegen && ./gradlew build |
| 66 | + |
| 67 | +# Publish to local Maven for downstream use |
| 68 | +cd codegen && ./gradlew publishToMavenLocal |
| 69 | +``` |
| 70 | + |
| 71 | +The codegen artifact version is fixed at `0.1.0` and is not published to |
| 72 | +Maven Central — you **MUST** `publishToMavenLocal`. |
| 73 | + |
| 74 | +## Runtime architecture |
| 75 | + |
| 76 | +### Middleware stack |
| 77 | + |
| 78 | +The operation pipeline is built on a middleware stack defined in `middleware/`. |
| 79 | +Steps execute in order: Initialize → Serialize → Build → Finalize → |
| 80 | +Deserialize. Each step is a `middleware.Step` that holds an ordered list of |
| 81 | +middleware. The codegen generates middleware registrations for each operation. |
| 82 | + |
| 83 | +### Encoding packages |
| 84 | + |
| 85 | +Each wire format has its own encoder/decoder under `encoding/`. These are |
| 86 | +low-level — they produce/consume raw tokens or values, not full Smithy shapes. |
| 87 | +Generated serde code calls into these packages. |
| 88 | + |
| 89 | +## Codegen: GoWriter and template system |
| 90 | + |
| 91 | +GoWriter extends Smithy's `SymbolWriter` and is the primary mechanism for |
| 92 | +generating Go source. It has **two distinct writing styles** that must not be |
| 93 | +confused. |
| 94 | + |
| 95 | +### Style 1: Positional args (`writer.write` / `writer.openBlock`) |
| 96 | + |
| 97 | +Inherited from `SymbolWriter`. Arguments are positional and referenced with |
| 98 | +`$`-prefixed format characters. Each `$X` consumes the next argument in order. |
| 99 | + |
| 100 | +Format characters: |
| 101 | +- `$L` — Literal (toString). Strings, names, anything that should be inserted |
| 102 | + verbatim. |
| 103 | +- `$S` — String, quoted. Wraps the value in Go double-quotes. |
| 104 | +- `$T` — Type (Symbol). Inserts the symbol name and auto-adds its import. |
| 105 | +- `$P` — Pointable type (Symbol). Like `$T` but prepends `*` if the symbol is |
| 106 | + marked pointable. |
| 107 | +- `$W` — Writable. Evaluates a `Writable` (lambda/closure) inline. |
| 108 | +- `$D` — Dependency. Adds a `GoDependency` import, expands to empty string. |
| 109 | + |
| 110 | +Numbered variants (`$1L`, `$2T`, etc.) allow reusing the same argument |
| 111 | +multiple times. The number is 1-indexed and refers to the position in the |
| 112 | +argument list: |
| 113 | + |
| 114 | +```java |
| 115 | +// $1L is used twice, $2L once — only 2 args needed |
| 116 | +writer.write("type $1L struct{}\nvar _ $2L = (*$1L)(nil)", |
| 117 | + DEFAULT_NAME, INTERFACE_NAME); |
| 118 | +``` |
| 119 | + |
| 120 | +`openBlock`/`closeBlock` manage indentation for braced blocks. Arguments are |
| 121 | +positional: |
| 122 | + |
| 123 | +```java |
| 124 | +writer.openBlock("func (c $P) $T(ctx $T) ($P, error) {", "}", |
| 125 | + serviceSymbol, operationSymbol, contextSymbol, outputSymbol, |
| 126 | + () -> { |
| 127 | + writer.write("return nil, nil"); |
| 128 | + }); |
| 129 | +``` |
| 130 | + |
| 131 | +### Style 2: Named template args (`goTemplate` / `writeGoTemplate`) |
| 132 | + |
| 133 | +Uses `$name:X` syntax where `name` is a key in a `Map<String, Object>` and `X` |
| 134 | +is the format character. Arguments are passed as one or more maps. This is the |
| 135 | +**preferred style for new code** — it is more readable and less error-prone |
| 136 | +than positional args. |
| 137 | + |
| 138 | +```java |
| 139 | +return goTemplate(""" |
| 140 | + func $name:L(v $cborValue:T) ($type:T, error) { |
| 141 | + return $coercer:T(v) |
| 142 | + } |
| 143 | + """, |
| 144 | + Map.of( |
| 145 | + "name", getDeserializerName(shape), |
| 146 | + "cborValue", SmithyGoTypes.Encoding.Cbor.Value, |
| 147 | + "type", symbolProvider.toSymbol(shape), |
| 148 | + "coercer", coercer |
| 149 | + )); |
| 150 | +``` |
| 151 | + |
| 152 | +Rules: |
| 153 | +- `goTemplate(String, Map...)` is a **static** method that returns a |
| 154 | + `Writable` (a `Consumer<GoWriter>` lambda). It does NOT write immediately. |
| 155 | +- `writeGoTemplate(String, Map...)` is an **instance** method that writes |
| 156 | + immediately to the writer. |
| 157 | +- Maps are merged into the writer's context scope for the duration of the |
| 158 | + template. Multiple maps can be passed and are applied in order. |
| 159 | +- The writer pre-populates common symbols in context: `fmt.Sprintf`, |
| 160 | + `fmt.Errorf`, `errors.As`, `context.Context`, `time.Now`. |
| 161 | + |
| 162 | +### Composing writables |
| 163 | + |
| 164 | +- `ChainWritable` — Collects multiple `Writable`s and composes them with |
| 165 | + newlines between each. Use `.compose()` (with newlines) or |
| 166 | + `.compose(false)` (without). |
| 167 | + |
| 168 | +### Symbol constants |
| 169 | + |
| 170 | +For symbols, use `SmithyGoDependency.*.valueSymbol("Name")` or |
| 171 | +`SmithyGoDependency.*.pointableSymbol("Name")`. |
| 172 | + |
0 commit comments