Skip to content

feat(tx3c): add the java-client template - #364

Merged
scarmuega merged 2 commits into
mainfrom
work/work-de755479921262fc570f0cd1b0b785cd01fec550
Sep 27, 2026
Merged

scarmuega merged 2 commits into
mainfrom
work/work-de755479921262fc570f0cd1b0b785cd01fec550

Conversation

@scarmuega

Copy link
Copy Markdown
Contributor

Summary

Adds the built-in java-client template to tx3c (tx3c codegen --template java-client), per decisions 0015 and 0016 and the design inputs bound in plans/lang-codegen-java-client.md.

  • Template — a standalone Maven project (land.tx3.generated:<protocol>-client:0.0.0-SNAPSHOT, depending on land.tx3:tx3-sdk:0.15.0): pom.xml, README.md, and src/main/java/land/tx3/generated/<protocol>/<Protocol>Client.java through templated output paths. The client wraps Tx3Client via Tx3ClientBuilder.fromParts, embeds one named TirEnvelope per transaction and one Profile per profile, takes a typed Profile enum when profiles exist, exposes with<Party> binders through withPartyUnchecked, and each transaction method sends its params through argTagged with statically built ArgValues. No TII, schema or ParamType reaches the generated source. SDK types are spelled fully qualified so a protocol type named like an SDK type (the edge fixture's Address) cannot shadow them.
  • Java backend — every value declaration is now public and carries a toArgValue() built from its shape (records/variant cases → struct with the constructor index, tuples → tuple, lists/maps convert their elements, maps sorted by key like the SDK's dynamic encoder). Aliases become one-field wrapper records (record Amount(java.math.BigInteger value)) instead of empty records, so the aliased value survives. Characters Java forbids in identifiers collapse to one _, which is what gives the protocol-named package segment via the method role.
  • Helpers — argValue <schema> <language> <expr> [<member>], stringLiteral <text> <language> (chunked with String.join beyond the 65535-byte class-file constant limit), indent <text> <columns>, kebabCase.
  • CI — java-client joins the codegen compile matrix. The script compiles the rendered transfer and complex fixtures with mvn verify and then runs a smoke consumer (.github/scripts/java-client-smoke/<fixture>/Smoke.java) inside the rendered project, so the client runs with no source TII present. Until land.tx3:tx3-sdk:0.15.0 is on Maven Central the check installs java-sdk commit d7a6dc4da56f73a7a47fe2e77444ad056885cfe7 as 0.15.0-SNAPSHOT and selects it with -Dtx3.sdk.version; the block to remove afterwards is marked in the script.

Golden output changes to review

The Java backend change re-blesses the existing Java goldens under tests/codegen/expected/custom/{java,compat-java,templated-paths}: declarations gain public and toArgValue(), and Opaque becomes record Opaque(land.tx3.sdk.ArgValue value). New goldens under expected/java-client/ cover transfer (two profiles, three parties), complex (one profile, every schema kind) and edge (no profiles, no parties, keyword transaction class, builtin-named component).

Verification

Local, Java 21 (Homebrew OpenJDK 21.0.12.1) against java-sdk d7a6dc4 installed as 0.15.0-SNAPSHOT:

  • cargo fmt --all -- --check, cargo clippy --workspace --all-targets --all-features --locked -- -D warnings, cargo test -p tx3c --locked (43 unit tests + golden corpus) pass; git diff --check clean.
  • bash .github/scripts/codegen-compile-check.sh java-client target/debug/tx3c passes end to end: both fixtures render, mvn -B -ntp verify succeeds, and both smoke consumers run (constructors for two profiles / one profile, typed party binders, typed transaction methods, variant constructor index and record field count asserted on toArgValue()).
  • The edge fixture also compiles with mvn verify (not part of CI, matching the other languages).
  • Rendering each fixture twice is byte-identical and matches the committed goldens.

Notes

  • Params records intentionally carry no toArgValue(): they are argument bags and the client sends each member as its own tagged argument.
  • A null-typed or unknown-typed parameter is typed land.tx3.sdk.ArgValue and passed through, following the approved fallback mapping.

🤖 Generated with Claude Code

`tx3c codegen --template java-client` renders a standalone Maven project:
`pom.xml`, `README.md`, and `src/main/java/land/tx3/generated/<protocol>/
<Protocol>Client.java`, laid out through templated output paths. The client
wraps the runtime SDK's `Tx3Client`: it embeds one `TirEnvelope` per
transaction and one `Profile` per profile, takes a typed `Profile` enum when
the protocol declares profiles, exposes `with<Party>` binders over
`withPartyUnchecked`, and builds each transaction's arguments statically
through `argTagged`, so no TII, schema or `ParamType` reaches the consumer.

The Java backend now spells that static construction. Every value
declaration is `public` and carries a `toArgValue()` built from its shape:
records and variant cases become `struct` with their constructor index,
tuples `tuple`, lists and maps convert their elements (maps sorted by key,
as the SDK's dynamic encoder does), and aliases become one-field wrapper
records so the aliased value survives. Identifier characters Java forbids
collapse to one underscore, which gives the protocol-named package segment.

New helpers: `argValue <schema> <language> <expr> [<member>]` spells a
parameter's tagged argument, `stringLiteral` escapes embedded text (joining
chunks at runtime past the class-file constant limit), `indent` nests a
rendered block, and `kebabCase` names the Maven artifact.

CI compiles the rendered transfer and complex fixtures with Maven and runs a
smoke consumer inside each rendered project. Until `land.tx3:tx3-sdk:0.15.0`
is on Maven Central the check installs java-sdk commit
d7a6dc4da56f73a7a47fe2e77444ad056885cfe7 as `0.15.0-SNAPSHOT` and selects it
through the generated `tx3.sdk.version` property.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@scarmuega
scarmuega marked this pull request as ready for review September 27, 2026 13:11
@scarmuega

scarmuega commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Merge observed.

Plan: plans/lang-codegen-java-client.md
QA-approved head: d330772527bb685afc876b867814a723d0704ab6

Trellis will verify the human merge evidence, adopt the domain pin, and retire the plan. No further merge action is needed.

Reconciles the java-client template with the swift-client template that
landed in #365. Both added static argument construction to the codegen
core in different shapes; the merge keeps main's `Encoding` model and
`argValue`/`member` backend surface and ports the Java backend onto it:

- `Backend::argument` spells from a member's `Encoding`; the shape-based
  `argument`/`accessor` pair and `Member::argument` are gone. Java overrides
  `member` for record accessors and keeps `sanitize` and `string_literal`.
- `Backend::declares_aliases` (Java: true) tells component resolution that
  an alias is a wrapper record converting itself, so a reference to an
  alias component spells `.toArgValue()` instead of the target's encoding.
- The Java template calls main's `argValue <tii> <tx> <param> <lang> <expr>`.
- Both templates are registered, listed in the CLI help and error, and in
  the CI codegen matrix and compile-check script.

Rendered java-client and swift-client output is byte-identical to the
committed goldens.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@scarmuega
scarmuega marked this pull request as draft September 27, 2026 13:59
@scarmuega
scarmuega marked this pull request as ready for review September 27, 2026 14:15
@scarmuega
scarmuega merged commit 456f685 into main Sep 27, 2026
16 checks passed
@scarmuega
scarmuega deleted the work/work-de755479921262fc570f0cd1b0b785cd01fec550 branch September 27, 2026 14:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant