Skip to content

Commit 53bfd0a

Browse files
committed
docs: add comprehensive documentation for generateEnumValueFunctions
- Document the purpose and implementation of enum utility generation - Explain the bidirectional lookup functionality - Provide usage examples and configuration options - Detail the AST generation process - Include performance considerations and best practices
1 parent 7162630 commit 53bfd0a

File tree

1 file changed

+231
-0
lines changed

1 file changed

+231
-0
lines changed

packages/proto-parser/ENUMS.md

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
# Enum Utilities in Proto Parser
2+
3+
## Overview
4+
5+
The `generateEnumValueFunctions` function is a key utility in the proto-parser package that generates TypeScript code for bidirectional enum value lookups. It creates a runtime utility function that can convert between enum string names and their numeric values.
6+
7+
## Function Location
8+
9+
- **File**: `src/ast/enums/enums.ts`
10+
- **Export**: Named export from the enums module
11+
12+
## Function Signature
13+
14+
```typescript
15+
export const generateEnumValueFunctions = (enumData: Enum[]) => {
16+
// Returns an array of two AST nodes:
17+
// 1. EnumType type alias export
18+
// 2. getEnumValue function export
19+
}
20+
```
21+
22+
## Purpose
23+
24+
The function generates a TypeScript utility that provides:
25+
26+
1. **Bidirectional enum lookups**: Convert enum names to values and values to names
27+
2. **Type safety**: Uses a union type of all enum names for compile-time checking
28+
3. **Runtime validation**: Throws descriptive errors for invalid inputs
29+
30+
## Generated Code Structure
31+
32+
### 1. EnumType Type Alias
33+
34+
```typescript
35+
export type EnumType = "Enum1" | "Enum2" | "Enum3" | ...;
36+
```
37+
38+
A union type of all enum names in the protobuf schema, providing type safety when calling the utility function.
39+
40+
### 2. getEnumValue Function
41+
42+
```typescript
43+
export const getEnumValue = (enumType: EnumType, key: string | number) => {
44+
switch (enumType) {
45+
case "EnumName":
46+
{
47+
switch (key) {
48+
case "ENUM_VALUE_1": return 0;
49+
case "ENUM_VALUE_2": return 1;
50+
case 0: return "ENUM_VALUE_1";
51+
case 1: return "ENUM_VALUE_2";
52+
default: throw new Error("Key not recognized in enum EnumName");
53+
}
54+
}
55+
// ... more enum cases
56+
default: throw new Error("Enum type not recognized");
57+
}
58+
}
59+
```
60+
61+
## Implementation Details
62+
63+
### AST Generation Process
64+
65+
1. **Type Union Creation**:
66+
- Creates a union type of all enum names as string literals
67+
- Exported as `EnumType` for type-safe enum selection
68+
69+
2. **Function Structure**:
70+
- Uses nested switch statements for performance
71+
- Outer switch: Selects the enum by name
72+
- Inner switch: Performs the bidirectional lookup
73+
74+
3. **Bidirectional Mapping**:
75+
- String → Number: `case "ENUM_VALUE": return numericValue;`
76+
- Number → String: `case numericValue: return "ENUM_VALUE";`
77+
78+
4. **Error Handling**:
79+
- Throws specific errors for unrecognized enum types
80+
- Throws specific errors for unrecognized keys within an enum
81+
82+
### Code Generation Using Babel AST
83+
84+
The function uses Babel's AST builders to construct the code:
85+
86+
```typescript
87+
// Type alias creation
88+
const enumTypeUnion = t.tsUnionType(
89+
enumData.map(enumDef => t.tsLiteralType(t.stringLiteral(enumDef.name)))
90+
);
91+
92+
// Function parameter with type annotation
93+
const enumTypeParam = t.identifier('enumType');
94+
enumTypeParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(enumTypeIdentifier));
95+
96+
// Switch case generation
97+
const outerCases = enumData.map(enumDef => {
98+
const innerCases = Object.entries(enumDef.values).map(([key, value]) => {
99+
// Forward mapping (string to number)
100+
return t.switchCase(t.stringLiteral(key), [t.returnStatement(t.numericLiteral(value))]);
101+
});
102+
103+
// Reverse mapping (number to string)
104+
innerCases.push(...Object.entries(enumDef.values).map(([key, value]) => {
105+
return t.switchCase(t.numericLiteral(value), [t.returnStatement(t.stringLiteral(key))]);
106+
}));
107+
108+
// ... error handling
109+
});
110+
```
111+
112+
## Usage Configuration
113+
114+
The enum utilities are controlled by the following configuration:
115+
116+
```typescript
117+
utils: {
118+
enums: {
119+
enabled: boolean; // Default: false
120+
filename: string; // Default: 'utils.ts'
121+
}
122+
}
123+
```
124+
125+
### Enabling Enum Utilities
126+
127+
```typescript
128+
const options = {
129+
utils: {
130+
enums: {
131+
enabled: true,
132+
filename: 'enum-utils.ts'
133+
}
134+
}
135+
};
136+
```
137+
138+
## Generated File Example
139+
140+
When enabled, the function generates a file (default: `utils.ts`) containing:
141+
142+
```typescript
143+
/**
144+
* This file was automatically generated by pg-proto-parser@latest.
145+
* DO NOT MODIFY IT BY HAND. Instead, modify the source proto file,
146+
* and run the pg-proto-parser generate command to regenerate this file.
147+
*/
148+
149+
export type EnumType = "OverridingKind" | "QuerySource" | "SortByDir" | ...;
150+
151+
export const getEnumValue = (enumType: EnumType, key: string | number) => {
152+
switch (enumType) {
153+
case "OverridingKind":
154+
{
155+
switch (key) {
156+
case "OVERRIDING_NOT_SET": return 0;
157+
case "OVERRIDING_USER_VALUE": return 1;
158+
case "OVERRIDING_SYSTEM_VALUE": return 2;
159+
case 0: return "OVERRIDING_NOT_SET";
160+
case 1: return "OVERRIDING_USER_VALUE";
161+
case 2: return "OVERRIDING_SYSTEM_VALUE";
162+
default: throw new Error("Key not recognized in enum OverridingKind");
163+
}
164+
}
165+
// ... more cases
166+
}
167+
}
168+
```
169+
170+
## Use Cases
171+
172+
### 1. Dynamic Enum Value Resolution
173+
174+
```typescript
175+
// Get numeric value from string
176+
const value = getEnumValue("OverridingKind", "OVERRIDING_USER_VALUE"); // Returns: 1
177+
178+
// Get string from numeric value
179+
const name = getEnumValue("OverridingKind", 1); // Returns: "OVERRIDING_USER_VALUE"
180+
```
181+
182+
### 2. Type-Safe Enum Operations
183+
184+
```typescript
185+
// TypeScript will enforce valid enum names
186+
getEnumValue("InvalidEnum", "VALUE"); // Type error!
187+
188+
// Runtime validation for values
189+
getEnumValue("OverridingKind", "INVALID_VALUE"); // Throws: "Key not recognized in enum OverridingKind"
190+
```
191+
192+
### 3. Protocol Message Parsing
193+
194+
Useful when parsing protocol buffer messages where you need to convert between wire format (numbers) and readable format (strings).
195+
196+
## Performance Considerations
197+
198+
1. **Switch Statement Optimization**: Modern JavaScript engines optimize switch statements well, making this approach efficient
199+
2. **No Object Allocation**: The function doesn't create intermediate objects, reducing memory pressure
200+
3. **Direct Returns**: Each case immediately returns, avoiding unnecessary computation
201+
202+
## Integration with Other Features
203+
204+
The enum utilities work alongside:
205+
206+
1. **Enum Declarations**: Regular TypeScript enum exports (`enums.ts`)
207+
2. **Enum Maps**: JSON/TypeScript enum mapping files (`enumMap` feature)
208+
3. **Type Definitions**: Interface definitions that use the enums
209+
210+
## Best Practices
211+
212+
1. **Enable When Needed**: Only enable if you need runtime enum conversions
213+
2. **Import Selectively**: Import only the `getEnumValue` function where needed
214+
3. **Type Safety**: Always use the `EnumType` type for the first parameter
215+
4. **Error Handling**: Wrap calls in try-catch if handling untrusted input
216+
217+
## Limitations
218+
219+
1. **File Size**: For schemas with many enums, the generated file can be large (4000+ lines)
220+
2. **No Partial Generation**: Currently generates utilities for all enums, not selectively
221+
3. **No Custom Names**: The function is always named `getEnumValue`
222+
223+
## Future Enhancements
224+
225+
Potential improvements could include:
226+
227+
1. **Selective Generation**: Generate utilities only for specified enums
228+
2. **Multiple Output Files**: Split large utility files
229+
3. **Custom Function Names**: Allow configuration of the generated function name
230+
4. **Tree Shaking**: Better support for dead code elimination
231+
5. **Enum Filtering**: Option to exclude certain enums from utility generation

0 commit comments

Comments
 (0)