Skip to content

Commit 22e0003

Browse files
committed
Add ParseField helper
This removes the duplication of parsing required for renamed fields and non-renamed fields.
1 parent 8c1ab97 commit 22e0003

File tree

1 file changed

+43
-99
lines changed

1 file changed

+43
-99
lines changed

src/select-query-parser.ts

Lines changed: 43 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,12 @@ type ConstructFieldDefinition<
249249
: Child[]
250250
: never
251251
}
252+
: Field extends { name: string; type: infer T }
253+
? { [K in Field['name']]: T }
252254
: Field extends { name: string; original: string }
253255
? Field['original'] extends keyof Row
254256
? { [K in Field['name']]: Row[Field['original']] }
255257
: SelectQueryError<`Referencing missing column \`${Field['original']}\``>
256-
: Field extends { name: string; type: infer T }
257-
? { [K in Field['name']]: T }
258258
: Record<string, unknown>
259259

260260
/**
@@ -318,40 +318,22 @@ type ParseIdentifier<Input extends string> = ReadLetters<Input> extends [
318318
: ParserError<`No (possibly double-quoted) identifier at \`${Input}\``>
319319

320320
/**
321-
* Parses a node.
322-
* A node is one of the following:
323-
* - `*`
321+
* Parses a field without preceding field renaming.
322+
* A field is one of the following:
324323
* - `field`
325324
* - `field::type`
326325
* - `field->json...`
327326
* - `field(nodes)`
328327
* - `field!hint(nodes)`
329328
* - `field!inner(nodes)`
330329
* - `field!hint!inner(nodes)`
331-
* - `renamed_field:field`
332-
* - `renamed_field:field::type`
333-
* - `renamed_field:field->json...`
334-
* - `renamed_field:field(nodes)`
335-
* - `renamed_field:field!hint(nodes)`
336-
* - `renamed_field:field!inner(nodes)`
337-
* - `renamed_field:field!hint!inner(nodes)`
338330
*
339-
* TODO: more support for JSON operators `->`, `->>`.
331+
* TODO: support type casting of JSON operators `a->b::type`, `a->>b::type`.
340332
*/
341-
type ParseNode<Input extends string> = Input extends ''
333+
type ParseField<Input extends string> = Input extends ''
342334
? ParserError<'Empty string'>
343-
: // `*`
344-
Input extends `*${infer Remainder}`
345-
? [{ star: true }, EatWhitespace<Remainder>]
346335
: ParseIdentifier<Input> extends [infer Name, `${infer Remainder}`]
347-
? EatWhitespace<Remainder> extends `::${infer Remainder}`
348-
? ParseIdentifier<Remainder> extends [infer CastType, `${infer Remainder}`]
349-
? // `field::type`
350-
CastType extends PostgreSQLTypes
351-
? [{ name: Name; type: TypeScriptTypes<CastType> }, EatWhitespace<Remainder>]
352-
: never
353-
: ParserError<`Unexpected type cast at \`${Input}\``>
354-
: EatWhitespace<Remainder> extends `!inner${infer Remainder}`
336+
? EatWhitespace<Remainder> extends `!inner${infer Remainder}`
355337
? ParseEmbeddedResource<EatWhitespace<Remainder>> extends [infer Fields, `${infer Remainder}`]
356338
? // `field!inner(nodes)`
357339
[{ name: Name; original: Name; children: Fields }, EatWhitespace<Remainder>]
@@ -383,79 +365,6 @@ type ParseNode<Input extends string> = Input extends ''
383365
'Expected embedded resource after `!hint`'
384366
>
385367
: ParserError<'Expected identifier after `!`'>
386-
: EatWhitespace<Remainder> extends `:${infer Remainder}`
387-
? ParseIdentifier<EatWhitespace<Remainder>> extends [infer OriginalName, `${infer Remainder}`]
388-
? EatWhitespace<Remainder> extends `::${infer Remainder}`
389-
? ParseIdentifier<Remainder> extends [infer CastType, `${infer Remainder}`]
390-
? // `renamed_field:field::type`
391-
CastType extends PostgreSQLTypes
392-
? [{ name: Name; type: TypeScriptTypes<CastType> }, EatWhitespace<Remainder>]
393-
: never
394-
: ParserError<`Unexpected type cast at \`${Input}\``>
395-
: EatWhitespace<Remainder> extends `!inner${infer Remainder}`
396-
? ParseEmbeddedResource<EatWhitespace<Remainder>> extends [
397-
infer Fields,
398-
`${infer Remainder}`
399-
]
400-
? // `renamed_field:field!inner(nodes)`
401-
[{ name: Name; original: OriginalName; children: Fields }, EatWhitespace<Remainder>]
402-
: CreateParserErrorIfRequired<
403-
ParseEmbeddedResource<EatWhitespace<Remainder>>,
404-
'Expected embedded resource after `!inner`'
405-
>
406-
: EatWhitespace<Remainder> extends `!${infer Remainder}`
407-
? ParseIdentifier<EatWhitespace<Remainder>> extends [infer Hint, `${infer Remainder}`]
408-
? EatWhitespace<Remainder> extends `!inner${infer Remainder}`
409-
? ParseEmbeddedResource<EatWhitespace<Remainder>> extends [
410-
infer Fields,
411-
`${infer Remainder}`
412-
]
413-
? // `renamed_field:field!hint!inner(nodes)`
414-
[
415-
{ name: Name; original: OriginalName; hint: Hint; children: Fields },
416-
EatWhitespace<Remainder>
417-
]
418-
: CreateParserErrorIfRequired<
419-
ParseEmbeddedResource<EatWhitespace<Remainder>>,
420-
'Expected embedded resource after `!inner`'
421-
>
422-
: ParseEmbeddedResource<EatWhitespace<Remainder>> extends [
423-
infer Fields,
424-
`${infer Remainder}`
425-
]
426-
? // `renamed_field:field!hint(nodes)`
427-
[
428-
{
429-
name: Name
430-
original: OriginalName
431-
hint: Hint
432-
children: Fields
433-
},
434-
EatWhitespace<Remainder>
435-
]
436-
: CreateParserErrorIfRequired<
437-
ParseEmbeddedResource<EatWhitespace<Remainder>>,
438-
'Expected embedded resource after `!hint`'
439-
>
440-
: ParserError<'Expected identifier after `!`'>
441-
: ParseEmbeddedResource<EatWhitespace<Remainder>> extends [
442-
infer Fields,
443-
`${infer Remainder}`
444-
]
445-
? // `renamed_field:field(nodes)`
446-
[{ name: Name; original: OriginalName; children: Fields }, EatWhitespace<Remainder>]
447-
: ParseJsonAccessor<EatWhitespace<Remainder>> extends [
448-
infer _PropertyName,
449-
infer PropertyType,
450-
`${infer Remainder}`
451-
]
452-
? // `renamed_field:field->json...`
453-
[{ name: Name; type: PropertyType }, EatWhitespace<Remainder>]
454-
: ParseEmbeddedResource<EatWhitespace<Remainder>> extends ParserError<string>
455-
? ParseEmbeddedResource<EatWhitespace<Remainder>>
456-
: // `renamed_field:field`
457-
[{ name: Name; original: OriginalName }, EatWhitespace<Remainder>]
458-
: ParseIdentifier<EatWhitespace<Remainder>>
459368
: ParseEmbeddedResource<EatWhitespace<Remainder>> extends [infer Fields, `${infer Remainder}`]
460369
? // `field(nodes)`
461370
[{ name: Name; original: Name; children: Fields }, EatWhitespace<Remainder>]
@@ -465,13 +374,48 @@ type ParseNode<Input extends string> = Input extends ''
465374
`${infer Remainder}`
466375
]
467376
? // `field->json...`
468-
[{ name: PropertyName; type: PropertyType }, EatWhitespace<Remainder>]
377+
[{ name: PropertyName; original: PropertyName; type: PropertyType }, EatWhitespace<Remainder>]
469378
: ParseEmbeddedResource<EatWhitespace<Remainder>> extends ParserError<string>
470379
? ParseEmbeddedResource<EatWhitespace<Remainder>>
380+
: EatWhitespace<Remainder> extends `::${infer Remainder}`
381+
? ParseIdentifier<Remainder> extends [`${infer CastType}`, `${infer Remainder}`]
382+
? // `field::type`
383+
CastType extends PostgreSQLTypes
384+
? [{ name: Name; type: TypeScriptTypes<CastType> }, EatWhitespace<Remainder>]
385+
: ParserError<`Invalid type for \`::\` operator \`${CastType}\``>
386+
: ParserError<`Invalid type for \`::\` operator at \`${Remainder}\``>
471387
: // `field`
472388
[{ name: Name; original: Name }, EatWhitespace<Remainder>]
473389
: ParserError<`Expected identifier at \`${Input}\``>
474390

391+
/**
392+
* Parses a node.
393+
* A node is one of the following:
394+
* - `*`
395+
* - a field, as defined above
396+
* - a renamed field, `renamed_field:field`
397+
*/
398+
type ParseNode<Input extends string> = Input extends ''
399+
? ParserError<'Empty string'>
400+
: // `*`
401+
Input extends `*${infer Remainder}`
402+
? [{ star: true }, EatWhitespace<Remainder>]
403+
: ParseIdentifier<Input> extends [infer Name, `${infer Remainder}`]
404+
? EatWhitespace<Remainder> extends `::${infer _Remainder}`
405+
? // `field::`
406+
// Special case to detect type-casting before renaming.
407+
ParseField<Input>
408+
: EatWhitespace<Remainder> extends `:${infer Remainder}`
409+
? // `renamed_field:`
410+
ParseField<EatWhitespace<Remainder>> extends [infer Field, `${infer Remainder}`]
411+
? Field extends { name: string }
412+
? [Prettify<Omit<Field, 'name'> & { name: Name }>, EatWhitespace<Remainder>]
413+
: ParserError<`Unable to parse renamed field`>
414+
: ParserError<`Unable to parse renamed field`>
415+
: // Otherwise, just parse it as a field without renaming.
416+
ParseField<Input>
417+
: ParserError<`Expected identifier at \`${Input}\``>
418+
475419
/**
476420
* Parses a JSON property accessor of the shape `->a->b->c`. The last accessor in
477421
* the series may convert to text by using the ->> operator instead of ->.

0 commit comments

Comments
 (0)