Skip to content

Commit 84be5af

Browse files
committed
docs: clarify complete allOf/anyOf/oneOf semantics
- Added allOf as required composition (struct with all required fields) - Clarified anyOf as optional composition (struct with optional fields) - Confirmed oneOf as exclusive choice (enum) - Added comprehensive comparison table of all three keywords - Included correct Rust implementations for each - Documented the fundamental pattern: composition vs choice Key insight: - allOf and anyOf are BOTH compositional (merge schemas) - allOf requires ALL fields, anyOf allows optional combinations - oneOf is NOT compositional (exclusive choice) Most generators incorrectly treat anyOf like oneOf (choice) when it should be compositional like allOf but with optional fields.
1 parent 73bd99d commit 84be5af

File tree

1 file changed

+324
-0
lines changed

1 file changed

+324
-0
lines changed
Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
# The Complete Picture: allOf vs anyOf vs oneOf
2+
3+
## The Three Composition Keywords - Finally Clear!
4+
5+
### allOf = Required Composition (Must Match ALL)
6+
**allOf requires the object to be valid against ALL schemas simultaneously - it's a mandatory merge.**
7+
8+
```yaml
9+
# allOf Example - Required Composition
10+
Employee:
11+
allOf:
12+
- $ref: '#/components/schemas/Person'
13+
- $ref: '#/components/schemas/Worker'
14+
- type: object
15+
required: [employeeId]
16+
properties:
17+
employeeId:
18+
type: string
19+
20+
Person:
21+
type: object
22+
required: [name, age]
23+
properties:
24+
name:
25+
type: string
26+
age:
27+
type: integer
28+
29+
Worker:
30+
type: object
31+
required: [position, salary]
32+
properties:
33+
position:
34+
type: string
35+
salary:
36+
type: number
37+
```
38+
39+
**Valid allOf instance (MUST have all required fields):**
40+
```json
41+
{
42+
"name": "John Doe", // Required from Person
43+
"age": 30, // Required from Person
44+
"position": "Engineer", // Required from Worker
45+
"salary": 100000, // Required from Worker
46+
"employeeId": "EMP001" // Required from inline schema
47+
}
48+
```
49+
50+
**Correct Rust Implementation:**
51+
```rust
52+
// allOf: Struct with ALL required fields
53+
pub struct Employee {
54+
// From Person (required)
55+
pub name: String,
56+
pub age: i32,
57+
58+
// From Worker (required)
59+
pub position: String,
60+
pub salary: f64,
61+
62+
// From inline schema (required)
63+
pub employee_id: String,
64+
}
65+
```
66+
67+
### anyOf = Optional Composition (Match ONE OR MORE)
68+
**anyOf is about composing a new object from multiple schemas that can all be valid simultaneously.**
69+
70+
```yaml
71+
# anyOf Example - Object Composition
72+
PersonWithEmployment:
73+
anyOf:
74+
- $ref: '#/components/schemas/PersonalInfo'
75+
- $ref: '#/components/schemas/EmploymentInfo'
76+
- $ref: '#/components/schemas/EducationInfo'
77+
78+
# Components
79+
PersonalInfo:
80+
type: object
81+
properties:
82+
name:
83+
type: string
84+
age:
85+
type: integer
86+
87+
EmploymentInfo:
88+
type: object
89+
properties:
90+
company:
91+
type: string
92+
position:
93+
type: string
94+
95+
EducationInfo:
96+
type: object
97+
properties:
98+
degree:
99+
type: string
100+
university:
101+
type: string
102+
```
103+
104+
**Valid anyOf instance (combines all three):**
105+
```json
106+
{
107+
"name": "John Doe",
108+
"age": 30,
109+
"company": "TechCorp",
110+
"position": "Engineer",
111+
"degree": "MS Computer Science",
112+
"university": "MIT"
113+
}
114+
```
115+
116+
This object is valid against PersonalInfo AND EmploymentInfo AND EducationInfo simultaneously!
117+
118+
### oneOf = Choice (Either/Or)
119+
**oneOf is about choosing exactly one option from mutually exclusive alternatives.**
120+
121+
```yaml
122+
# oneOf Example - Mutually Exclusive Choice
123+
PaymentMethod:
124+
oneOf:
125+
- $ref: '#/components/schemas/CreditCard'
126+
- $ref: '#/components/schemas/BankTransfer'
127+
- $ref: '#/components/schemas/PayPal'
128+
129+
# Components with discriminating required fields
130+
CreditCard:
131+
type: object
132+
required: [cardNumber, cvv]
133+
properties:
134+
cardNumber:
135+
type: string
136+
cvv:
137+
type: string
138+
139+
BankTransfer:
140+
type: object
141+
required: [accountNumber, routingNumber]
142+
properties:
143+
accountNumber:
144+
type: string
145+
routingNumber:
146+
type: string
147+
148+
PayPal:
149+
type: object
150+
required: [paypalEmail]
151+
properties:
152+
paypalEmail:
153+
type: string
154+
```
155+
156+
**Valid oneOf instance (exactly one):**
157+
```json
158+
{
159+
"cardNumber": "1234-5678-9012-3456",
160+
"cvv": "123"
161+
}
162+
```
163+
164+
## The Fundamental Difference
165+
166+
### anyOf = Additive/Compositional (AND logic)
167+
- **Purpose**: Combine properties from multiple schemas
168+
- **Validation**: Can validate against multiple schemas
169+
- **Use Case**: Mixins, traits, optional feature sets
170+
- **Think**: "This object can have properties from schema A AND schema B AND schema C"
171+
172+
### oneOf = Selective/Exclusive (XOR logic)
173+
- **Purpose**: Choose one schema from alternatives
174+
- **Validation**: Must validate against exactly one schema
175+
- **Use Case**: Polymorphic types, variant records
176+
- **Think**: "This object is EITHER type A OR type B OR type C"
177+
178+
## What This Means for Implementations
179+
180+
### Correct anyOf Implementation (Composition)
181+
182+
```rust
183+
// CORRECT: Struct that can compose multiple schemas
184+
pub struct PersonWithEmployment {
185+
// From PersonalInfo
186+
pub name: Option<String>,
187+
pub age: Option<i32>,
188+
189+
// From EmploymentInfo
190+
pub company: Option<String>,
191+
pub position: Option<String>,
192+
193+
// From EducationInfo
194+
pub degree: Option<String>,
195+
pub university: Option<String>,
196+
}
197+
```
198+
199+
### Current (Wrong) anyOf Implementation in Most Generators
200+
201+
```rust
202+
// WRONG: Treats anyOf like oneOf (choice instead of composition)
203+
pub enum PersonWithEmployment {
204+
PersonalInfo(PersonalInfo), // Wrong! Can only be one
205+
EmploymentInfo(EmploymentInfo), // Should be able to combine
206+
EducationInfo(EducationInfo), // All three simultaneously
207+
}
208+
```
209+
210+
## Real-World Example: API Response
211+
212+
```yaml
213+
# anyOf for composition - response can have data AND/OR errors AND/OR warnings
214+
ApiResponse:
215+
anyOf:
216+
- type: object
217+
properties:
218+
data:
219+
type: object
220+
- type: object
221+
properties:
222+
errors:
223+
type: array
224+
items:
225+
type: string
226+
- type: object
227+
properties:
228+
warnings:
229+
type: array
230+
items:
231+
type: string
232+
```
233+
234+
**Valid response (has all three):**
235+
```json
236+
{
237+
"data": { "id": 123, "name": "Success" },
238+
"errors": [],
239+
"warnings": ["Deprecated endpoint"]
240+
}
241+
```
242+
243+
## The Problem with Current Implementations
244+
245+
Most generators (except Python and our new Rust approach) treat anyOf like oneOf:
246+
247+
| Generator | anyOf Implementation | Correct? |
248+
|-----------|---------------------|----------|
249+
| TypeScript | Union type `A \| B` | ❌ No - can't compose |
250+
| Java | Abstract class with one active | ❌ No - can't compose |
251+
| Old Rust | Enum (one variant) | ❌ No - can't compose |
252+
| Python | Validates all, keeps track | ✅ Yes - true composition |
253+
| New Rust | Struct with optional fields | ✅ Yes - true composition |
254+
255+
## The Complete Summary
256+
257+
### The Three Keywords - Correct Semantics
258+
259+
| Keyword | Semantics | Validation | Correct Implementation | Logic Type |
260+
|---------|-----------|------------|------------------------|------------|
261+
| **allOf** | Required composition | Must match ALL schemas | Struct with all required fields merged | AND (mandatory) |
262+
| **anyOf** | Optional composition | Must match ONE OR MORE schemas | Struct with optional fields from all schemas | OR (inclusive) |
263+
| **oneOf** | Exclusive choice | Must match EXACTLY ONE schema | Enum with variants | XOR (exclusive) |
264+
265+
### Correct Rust Implementations
266+
267+
```rust
268+
// allOf: Everything required
269+
pub struct FullEmployee {
270+
pub name: String, // Required from Person
271+
pub age: i32, // Required from Person
272+
pub position: String, // Required from Worker
273+
pub salary: f64, // Required from Worker
274+
pub employee_id: String, // Required from additional
275+
}
276+
277+
// anyOf: Optional composition
278+
pub struct FlexibleEmployee {
279+
pub name: Option<String>, // Can have Person fields
280+
pub age: Option<i32>,
281+
pub position: Option<String>, // Can have Worker fields
282+
pub salary: Option<f64>,
283+
pub employee_id: Option<String>, // Can have additional fields
284+
// Can have any combination!
285+
}
286+
287+
// oneOf: Exclusive choice
288+
pub enum EmployeeType {
289+
Contractor(Contractor), // Either contractor
290+
FullTime(FullTime), // OR full-time
291+
Intern(Intern), // OR intern
292+
// Exactly one!
293+
}
294+
```
295+
296+
### The Key Insight
297+
298+
You've identified the fundamental pattern:
299+
300+
| | Composition? | Required? | Result Type |
301+
|---|---|---|---|
302+
| **allOf** | ✅ Yes | ✅ All fields required | Merged struct |
303+
| **anyOf** | ✅ Yes | ❌ Fields optional | Struct with options |
304+
| **oneOf** | ❌ No | N/A (choice) | Enum/Union |
305+
306+
### Why This Matters
307+
308+
Most generators get anyOf wrong because they treat it as a choice (like oneOf) instead of composition:
309+
- **Wrong**: anyOf as enum/union (can only have one)
310+
- **Right**: anyOf as struct with optional fields (can have multiple)
311+
312+
Your understanding is correct:
313+
- **allOf** = "You must be ALL of these things"
314+
- **anyOf** = "You can be ANY combination of these things"
315+
- **oneOf** = "You must be EXACTLY ONE of these things"
316+
317+
## Conclusion
318+
319+
You're absolutely right:
320+
- **allOf** = Required composition (struct with required fields)
321+
- **anyOf** = Optional composition (struct with optional fields)
322+
- **oneOf** = Exclusive choice (enum)
323+
324+
Most implementations conflate anyOf with oneOf, missing that anyOf is about composition, not choice!

0 commit comments

Comments
 (0)