guardrail-dev / guardrail-dev/guardrail
Add Scala 3 Code Generation Support
- Dominant language
- Scala
- Stars
- 541
- Forks
- 138
- PR merge metrics
- No merged PRs in 30d
Description
# Proposal: Add Scala 3 Code Generation Support
## Problem Statement
Guardrail currently generates Scala 2 code only. Users with Scala 3 projects cannot use guardrail-generated code without compatibility issues due to Scala 2-specific syntax (implicit parameter clauses, implicit values/definitions, package objects).
## Proposed Solution
Add configuration option to generate Scala 3-compatible code while keeping guardrail's codebase on Scala 2.13.
### Key Changes
1. **Configuration**: Add `--scala-version` CLI flag and plugin settings to specify target Scala version (2.12, 2.13, 3)
2. **Generated Code Transformations** (when targeting Scala 3):
- `implicit` parameter clauses → `using` clauses
- `implicit val` definitions → `given` instances
- `implicit def` definitions → `given` instances
- Package objects → Regular objects (or top-level definitions)
- Maintain semantic equivalence with Scala 2 output
- Note: No implicit conversion (the language feature) is used in generated code
3. **Scope**: Update Scala framework generators (Akka HTTP, http4s, Scala Dropwizard)
- Java generators (Spring MVC, Java Dropwizard) generate Java code and are unaffected
- Pekko HTTP (external repo: guardrail-dev/guardrail-pekko-http) will need similar updates
## Benefits
- Scala 3 projects can use guardrail-generated code
- No breaking changes to existing Scala 2 generation (feature flag approach)
- Most runtime dependencies Scala 3 compatible:
- ✅ cats 2.10.0 - full Scala 3 support
- ✅ circe 0.14.6 - full Scala 3 support
- ✅ http4s 0.23.24 - full Scala 3 support
- ⚠️ akka-http 10.2.10 - uses Scala 2.13 binaries via `CrossVersion.for3Use2_13` (native Scala 3 support in 10.5.0+)
- ⚠️ refined 0.11.0 - Scala 3 artifacts available but with limited macro functionality
- No architectural changes to guardrail internals required
## Implementation
- **Approach**: Create syntax helper utilities, update ~7 key generator files (~250 update sites)
- **Testing**: Add Scala 3 to CI matrix, sample projects for each framework
- **Compatibility**: Maintain full backward compatibility with Scala 2 generation
## Examples
### CLI Usage
```bash
guardrail --scala-version 3 --specPath petstore.yaml --outputPath src/
```
### sbt Plugin
```scala
guardrailTasks := List(
ScalaServer(
file("petstore.yaml"),
pkg = "petstore",
framework = "http4s",
scalaVersion = "3"
)
)
```
### Generated Code Comparison
**Scala 2**:
```scala
implicit val encoder: Encoder[Pet] = deriveEncoder
def getPet(id: Long)(implicit ec: ExecutionContext): Future[Pet]
```
**Scala 3**:
```scala
given Encoder[Pet] = deriveEncoder
def getPet(id: Long)(using ec: ExecutionContext): Future[Pet]
```
## Non-Goals
- Migrating guardrail's internal codebase to Scala 3 (future work)
- Using Scala 3-exclusive features (enums, union types) - can be added later
- Breaking changes to existing APIs
## Open Questions
1. Should Scala 3 generation be opt-in or should we auto-detect from project settings?
2. What should be the default behavior if no version specified?
3. Do we need version-specific OpenAPI extension (x-scala-version)?
## Volunteering
We from Scala Teams are willing to contribute to implement this proposal if accepted by maintainers.
## Next Steps
1. Community feedback on approach
2. Implement foundation (ScalaVersion config, utilities)
3. Update generators systematically
4. Add comprehensive testing
5. Update documentation and examples
6. Coordinate with build plugin repositories (sbt-guardrail, maven, gradle)
---
## Technical Appendix: Implementation Scope
### Actual Code Locations Requiring Changes
Based on codebase analysis, the following specific locations need updates:
#### Implicit Parameter Clauses (5 locations):
1. `modules/scala-akka-http/src/main/scala/.../AkkaHttpClientGenerator.scala` - Lines 511, 556
2. `modules/scala-akka-http/src/main/scala/.../AkkaHttpServerGenerator.scala` - Lines 298, 456
3. `modules/scala-http4s/src/main/scala/.../Http4sClientGenerator.scala` - Line 499
**Change**: `Term.ParamClause(params, Some(Mod.Implicit()))` → `Scala3Compat.implicitsClause(params, scalaVersion)`
#### Implicit Value/Definition Generation (~250 locations):
1. **CirceProtocolGenerator** (~50 sites) - Encoder/Decoder instances
2. **CirceRefinedProtocolGenerator** (~45 sites) - Refined type codecs
3. **JacksonProtocolGenerator** (~110 sites) - Jackson serializers
4. **ScalaGenerator** (~40 sites) - Type class instances, Show/Eq instances
5. **AkkaHttp/Http4s marshallers** (~5 sites) - HTTP marshallers
**Change**: `q"implicit val x: T = ..."` → `Scala3Compat.implicitVal(Some(name), tpe, rhs, scalaVersion)`
#### Package Object Generation (1 location):
- `modules/scala-support/src/main/scala/.../ScalaGenerator.scala` - Line 378
**Change**: Generate top-level definitions for Scala 3 instead of package object
**Total**: ~7 generator files, ~250 systematic replacements
### Key Technical Facts
1. **No implicit conversions** used - Only implicit parameters and implicit values
2. **Explicit codec generation** - No auto-derivation, all codecs are hand-crafted
3. **No custom templates** - Programmatic scala-meta AST generation only
4. **Single package object** - Minimal migration surface area
5. **Mechanical transformation** - All changes are systematic replacements
This makes the implementation **low-risk** and **well-scoped**.
Contributor guide
Assessment
This issue has not been assessed yet.