Skip to content

Commit f3fdfed

Browse files
committed
New Feature: add handlersStyle option
1 parent 54e2a73 commit f3fdfed

File tree

3 files changed

+136
-37
lines changed

3 files changed

+136
-37
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ console.log(run('data Constrained (A :: string) = Fetching | GotData A'))
100100
# Options
101101

102102
```ts
103-
// ast.ts
103+
// fp-ts-codegen/lib/ast module
104104
105105
export interface Options {
106106
/** the name of the field used as tag */
@@ -109,12 +109,18 @@ export interface Options {
109109
foldName: string
110110
/** the name used for the input of pattern matching functions */
111111
matcheeName: string
112+
/**
113+
* the pattern matching handlers can be expressed as positional arguments
114+
* or a single object literal `tag -> handler`
115+
*/
116+
handlersStyle: { type: 'positional' } | { type: 'record'; handlersName: string }
112117
}
113118
114119
export const defaultOptions: Options = {
115120
tagName: 'type',
116121
foldName: 'fold',
117-
matcheeName: 'fa'
122+
matcheeName: 'fa',
123+
handlersStyle: { type: 'positional' }
118124
}
119125
```
120126

src/ast.ts

Lines changed: 104 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,27 @@ export interface Options {
1212
foldName: string
1313
/** the name used for the input of pattern matching functions */
1414
matcheeName: string
15+
/**
16+
* the pattern matching handlers can be expressed as positional arguments
17+
* or a single object literal `tag -> handler`
18+
*/
19+
handlersStyle: { type: 'positional' } | { type: 'record'; handlersName: string }
1520
}
1621

1722
export const defaultOptions: Options = {
1823
tagName: 'type',
1924
foldName: 'fold',
20-
matcheeName: 'fa'
25+
matcheeName: 'fa',
26+
handlersStyle: { type: 'positional' }
2127
}
2228

2329
const getLens = Lens.fromProp<Options>()
2430

2531
export const lenses: { [K in keyof Options]: Lens<Options, Options[K]> } = {
2632
tagName: getLens('tagName'),
2733
foldName: getLens('foldName'),
28-
matcheeName: getLens('matcheeName')
34+
matcheeName: getLens('matcheeName'),
35+
handlersStyle: getLens('handlersStyle')
2936
}
3037

3138
export interface AST<A> extends Reader<Options, A> {}
@@ -198,9 +205,96 @@ const getHandlerName = (c: M.Constructor): string => {
198205
return `on${c.name}`
199206
}
200207

208+
const getPositionalFoldHandlers = (d: M.Data, isEager: boolean): Array<ts.ParameterDeclaration> => {
209+
const returnTypeParameterName = getFoldReturnTypeParameterName(d.introduction)
210+
return d.constructors.toArray().map(c => {
211+
const type =
212+
isEager && isNullaryConstructor(c)
213+
? ts.createTypeReferenceNode(returnTypeParameterName, [])
214+
: ts.createFunctionTypeNode(
215+
undefined,
216+
c.members.map((m, position) => getParameterDeclaration(getMemberName(m, position), getType(m.type))),
217+
ts.createTypeReferenceNode(returnTypeParameterName, [])
218+
)
219+
return getParameterDeclaration(getHandlerName(c), type)
220+
})
221+
}
222+
223+
const getRecordFoldHandlers = (d: M.Data, handlersName: string, isEager: boolean): Array<ts.ParameterDeclaration> => {
224+
const returnTypeParameterName = getFoldReturnTypeParameterName(d.introduction)
225+
const type = ts.createTypeLiteralNode(
226+
d.constructors.toArray().map(c => {
227+
const type =
228+
isEager && isNullaryConstructor(c)
229+
? ts.createTypeReferenceNode(returnTypeParameterName, [])
230+
: ts.createFunctionTypeNode(
231+
undefined,
232+
c.members.map((m, position) => getParameterDeclaration(getMemberName(m, position), getType(m.type))),
233+
ts.createTypeReferenceNode(returnTypeParameterName, [])
234+
)
235+
return ts.createPropertySignature(undefined, getHandlerName(c), undefined, type, undefined)
236+
})
237+
)
238+
return [getParameterDeclaration(handlersName, type)]
239+
}
240+
241+
const getPositionalFoldBody = (d: M.Data, matcheeName: string, tagName: string, isEager: boolean) => {
242+
return ts.createBlock([
243+
ts.createSwitch(
244+
ts.createPropertyAccess(ts.createIdentifier(matcheeName), tagName),
245+
ts.createCaseBlock(
246+
d.constructors.toArray().map(c => {
247+
const access = ts.createIdentifier(getHandlerName(c))
248+
return ts.createCaseClause(ts.createStringLiteral(c.name), [
249+
ts.createReturn(
250+
isEager && isNullaryConstructor(c)
251+
? access
252+
: ts.createCall(
253+
access,
254+
[],
255+
c.members.map((m, position) => {
256+
return ts.createPropertyAccess(ts.createIdentifier(matcheeName), getMemberName(m, position))
257+
})
258+
)
259+
)
260+
])
261+
})
262+
)
263+
)
264+
])
265+
}
266+
267+
const getRecordFoldBody = (d: M.Data, matcheeName: string, tagName: string, handlersName: string, isEager: boolean) => {
268+
return ts.createBlock([
269+
ts.createSwitch(
270+
ts.createPropertyAccess(ts.createIdentifier(matcheeName), tagName),
271+
ts.createCaseBlock(
272+
d.constructors.toArray().map(c => {
273+
const access = ts.createPropertyAccess(ts.createIdentifier(handlersName), getHandlerName(c))
274+
return ts.createCaseClause(ts.createStringLiteral(c.name), [
275+
ts.createReturn(
276+
isEager && isNullaryConstructor(c)
277+
? access
278+
: ts.createCall(
279+
access,
280+
[],
281+
c.members.map((m, position) => {
282+
return ts.createPropertyAccess(ts.createIdentifier(matcheeName), getMemberName(m, position))
283+
})
284+
)
285+
)
286+
])
287+
})
288+
)
289+
)
290+
])
291+
}
292+
201293
const getFold = (d: M.Data, name: string, isEager: boolean): AST<ts.FunctionDeclaration> => {
202294
return new Reader(e => {
203295
const matcheeName = e.matcheeName
296+
const tagName = e.tagName
297+
const handlersStyle = e.handlersStyle
204298
const returnTypeParameterName = getFoldReturnTypeParameterName(d.introduction)
205299
const typeParameters = d.introduction.parameters
206300
.concat(M.parameter(returnTypeParameterName, none))
@@ -212,41 +306,16 @@ const getFold = (d: M.Data, name: string, isEager: boolean): AST<ts.FunctionDecl
212306
d.introduction.parameters.map(p => ts.createTypeReferenceNode(p.name, []))
213307
)
214308
)
215-
const handlers = d.constructors.toArray().map(c => {
216-
const type =
217-
isEager && isNullaryConstructor(c)
218-
? ts.createTypeReferenceNode(returnTypeParameterName, [])
219-
: ts.createFunctionTypeNode(
220-
undefined,
221-
c.members.map((m, position) => getParameterDeclaration(getMemberName(m, position), getType(m.type))),
222-
ts.createTypeReferenceNode(returnTypeParameterName, [])
223-
)
224-
return getParameterDeclaration(getHandlerName(c), type)
225-
})
309+
const handlers =
310+
handlersStyle.type === 'positional'
311+
? getPositionalFoldHandlers(d, isEager)
312+
: getRecordFoldHandlers(d, handlersStyle.handlersName, isEager)
226313
const parameters = [matchee, ...handlers]
227314
const type = ts.createTypeReferenceNode(returnTypeParameterName, [])
228-
const body = ts.createBlock([
229-
ts.createSwitch(
230-
ts.createPropertyAccess(ts.createIdentifier(matcheeName), e.tagName),
231-
ts.createCaseBlock(
232-
d.constructors.toArray().map(c => {
233-
return ts.createCaseClause(ts.createStringLiteral(c.name), [
234-
ts.createReturn(
235-
isEager && isNullaryConstructor(c)
236-
? ts.createIdentifier(getHandlerName(c))
237-
: ts.createCall(
238-
ts.createIdentifier(getHandlerName(c)),
239-
[],
240-
c.members.map((m, position) => {
241-
return ts.createPropertyAccess(ts.createIdentifier(matcheeName), getMemberName(m, position))
242-
})
243-
)
244-
)
245-
])
246-
})
247-
)
248-
)
249-
])
315+
const body =
316+
handlersStyle.type === 'positional'
317+
? getPositionalFoldBody(d, matcheeName, tagName, isEager)
318+
: getRecordFoldBody(d, matcheeName, tagName, handlersStyle.handlersName, isEager)
250319
return getFunctionDeclaration(name, typeParameters, parameters, type, body)
251320
})
252321
}

test/printer.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,30 @@ export function foldL<A, R>(fa: Option<A>, onNone: () => R, onSome: (value0: A)
279279
lenses.matcheeName.set('input')(defaultOptions)
280280
)
281281
})
282+
283+
it('should handle handlersName + handlersStyle', () => {
284+
assertEqual(
285+
P.fold,
286+
H.Option,
287+
[
288+
`export function fold<A, R>(fa: Option<A>, clauses: {
289+
onNone: R;
290+
onSome: (value0: A) => R;
291+
}): R { switch (fa.type) {
292+
case "None": return clauses.onNone;
293+
case "Some": return clauses.onSome(fa.value0);
294+
} }`,
295+
`export function foldL<A, R>(fa: Option<A>, clauses: {
296+
onNone: () => R;
297+
onSome: (value0: A) => R;
298+
}): R { switch (fa.type) {
299+
case "None": return clauses.onNone();
300+
case "Some": return clauses.onSome(fa.value0);
301+
} }`
302+
],
303+
lenses.handlersStyle.set({ type: 'record', handlersName: 'clauses' })(defaultOptions)
304+
)
305+
})
282306
})
283307
})
284308
})

0 commit comments

Comments
 (0)