Skip to content

Commit 86ff272

Browse files
committed
docs: add composition-based alternative for allOf support
Enhanced the type mapping documentation with: - Alternative composition approach for allOf using serde flatten - Explanation of how to avoid property name conflicts - Generated names for anonymous objects (Object$1, Object$2) - Trade-offs between flattening vs composition approaches This approach would maintain type safety while avoiding the complex property merging issues that arise with allOf schemas.
1 parent 1f178e3 commit 86ff272

File tree

1 file changed

+65
-5
lines changed

1 file changed

+65
-5
lines changed

docs/generators/rust-type-mapping.md

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ Employee:
145145
```
146146
147147
```rust
148-
// What SHOULD be generated (but isn't)
148+
// Option 1: What SHOULD ideally be generated (flattened struct)
149149
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
150150
pub struct Employee {
151151
// Fields from Person
@@ -155,10 +155,29 @@ pub struct Employee {
155155
pub employee_id: String,
156156
pub department: String,
157157
}
158+
159+
// Option 2: Alternative with composition (avoiding property conflicts)
160+
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
161+
pub struct Employee {
162+
#[serde(flatten)]
163+
pub person: Person,
164+
165+
#[serde(flatten)]
166+
pub object_1: EmployeeObject1, // Anonymous object gets generated name
167+
}
168+
169+
// Generated for the anonymous object in allOf
170+
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
171+
pub struct EmployeeObject1 {
172+
pub employee_id: String,
173+
pub department: String,
174+
}
158175
```
159176

160177
**Current behavior**: The generator fails or produces incorrect output for allOf schemas.
161178

179+
**Note**: Option 2 avoids property name conflicts that could occur if multiple schemas in allOf define the same property names. This approach maintains type safety while preserving the composition structure.
180+
162181
### Nullable Handling
163182

164183
```yaml
@@ -195,7 +214,44 @@ pub struct Node {
195214

196215
## Alternatives Considered (But Not Implemented)
197216

198-
### Alternative 1: True anyOf Support with Validation Trait
217+
### Alternative 1: Composition-based allOf Support
218+
219+
For allOf schemas, instead of trying to flatten all properties into a single struct (which can cause naming conflicts), use composition with serde's flatten:
220+
221+
```rust
222+
// Alternative: Composition approach for allOf
223+
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
224+
pub struct Employee {
225+
#[serde(flatten)]
226+
pub person: Person,
227+
228+
#[serde(flatten)]
229+
pub additional_props: EmployeeAdditional,
230+
}
231+
232+
// Benefits:
233+
// 1. Avoids property name conflicts
234+
// 2. Maintains clear composition structure
235+
// 3. Works with serde's existing flatten feature
236+
// 4. Each component can be validated independently
237+
238+
// Challenges:
239+
// 1. Generated names for anonymous objects (Object$1, Object$2, etc.)
240+
// 2. Requires serde flatten support
241+
// 3. May need special handling for required vs optional fields
242+
```
243+
244+
**Pros**:
245+
- Avoids property naming conflicts
246+
- Clear composition structure
247+
- Leverages serde's existing features
248+
249+
**Cons**:
250+
- Generated names for anonymous schemas
251+
- More complex serialization
252+
- Potential issues with nested flattening
253+
254+
### Alternative 2: True anyOf Support with Validation Trait
199255

200256
Instead of treating anyOf as oneOf, we could generate a validation trait:
201257

@@ -341,9 +397,13 @@ The initial implementation chose to convert anyOf to oneOf because:
341397
### Why No allOf Support?
342398

343399
1. **Composition Complexity**: Rust doesn't have built-in struct composition/inheritance
344-
2. **Serde Challenges**: Flattening multiple schemas into one struct is complex
345-
3. **Conflicting Fields**: Handling field name conflicts is non-trivial
346-
4. **Priority**: Less commonly used than oneOf in practice
400+
2. **Serde Challenges**: While serde supports `#[serde(flatten)]`, handling it in code generation is complex
401+
3. **Conflicting Fields**: When multiple schemas define the same property names, resolution is non-trivial:
402+
- Option A: Merge and error on conflicts (strict)
403+
- Option B: Last-wins override (loose but surprising)
404+
- Option C: Composition with generated names (Object$1, Object$2) - avoids conflicts but less ergonomic
405+
4. **Anonymous Schema Naming**: allOf often contains inline anonymous schemas that need generated names
406+
5. **Priority**: Less commonly used than oneOf in practice
347407

348408
## To-Be: Proposed Changes (PR #21915)
349409

0 commit comments

Comments
 (0)