@@ -38,14 +38,17 @@ type Digit = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0'
38
38
39
39
type Letter = Alphabet | Digit | '_'
40
40
41
+ type Json = string | number | boolean | null | { [ key : string ] : Json } | Json [ ]
42
+
41
43
// /**
42
44
// * Parsed node types.
43
45
// * Currently only `*` and all other fields.
44
46
// */
45
47
// type ParsedNode =
46
48
// | { star: true }
47
49
// | { name: string; original: string }
48
- // | { name: string; foreignTable: true };
50
+ // | { name: string; foreignTable: true }
51
+ // | { name: string; type: T };
49
52
50
53
/**
51
54
* Parser errors.
@@ -90,6 +93,8 @@ type ConstructFieldDefinition<
90
93
}
91
94
: Field extends { name : string ; original : string }
92
95
? { [ K in Field [ 'name' ] ] : Row [ Field [ 'original' ] ] }
96
+ : Field extends { name : string ; type : infer T }
97
+ ? { [ K in Field [ 'name' ] ] : T }
93
98
: Record < string , unknown >
94
99
95
100
/**
@@ -131,17 +136,19 @@ type ParseIdentifier<Input extends string> = ReadLetters<Input>
131
136
* A node is one of the following:
132
137
* - `*`
133
138
* - `field`
139
+ * - `field->json...`
134
140
* - `field(nodes)`
135
141
* - `field!hint(nodes)`
136
142
* - `field!inner(nodes)`
137
143
* - `field!hint!inner(nodes)`
138
144
* - `renamed_field:field`
145
+ * - `renamed_field:field->json...`
139
146
* - `renamed_field:field(nodes)`
140
147
* - `renamed_field:field!hint(nodes)`
141
148
* - `renamed_field:field!inner(nodes)`
142
149
* - `renamed_field:field!hint!inner(nodes)`
143
150
*
144
- * TODO: casting operators `::text`, JSON operators `->`, `->>`.
151
+ * TODO: casting operators `::text`, more support for JSON operators `->`, `->>`.
145
152
*/
146
153
type ParseNode < Input extends string > = Input extends ''
147
154
? ParserError < 'Empty string' >
@@ -225,6 +232,13 @@ type ParseNode<Input extends string> = Input extends ''
225
232
]
226
233
? // `renamed_field:field(nodes)`
227
234
[ { name : Name ; original : OriginalName ; children : Fields } , EatWhitespace < Remainder > ]
235
+ : ParseJsonAccessor < EatWhitespace < Remainder > > extends [
236
+ infer _PropertyName ,
237
+ infer PropertyType ,
238
+ `${infer Remainder } `
239
+ ]
240
+ ? // `renamed_field:field->json...`
241
+ [ { name : Name ; type : PropertyType } , EatWhitespace < Remainder > ]
228
242
: ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
229
243
? ParseEmbeddedResource < EatWhitespace < Remainder > >
230
244
: // `renamed_field:field`
@@ -233,12 +247,42 @@ type ParseNode<Input extends string> = Input extends ''
233
247
: ParseEmbeddedResource < EatWhitespace < Remainder > > extends [ infer Fields , `${infer Remainder } `]
234
248
? // `field(nodes)`
235
249
[ { name : Name ; original : Name ; children : Fields } , EatWhitespace < Remainder > ]
250
+ : ParseJsonAccessor < EatWhitespace < Remainder > > extends [
251
+ infer PropertyName ,
252
+ infer PropertyType ,
253
+ `${infer Remainder } `
254
+ ]
255
+ ? // `field->json...`
256
+ [ { name : PropertyName ; type : PropertyType } , EatWhitespace < Remainder > ]
236
257
: ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
237
258
? ParseEmbeddedResource < EatWhitespace < Remainder > >
238
259
: // `field`
239
260
[ { name : Name ; original : Name } , EatWhitespace < Remainder > ]
240
261
: ParserError < `Expected identifier at \`${Input } \``>
241
262
263
+ /**
264
+ * Parses a JSON property accessor of the shape `->a->b->c`. The last accessor in
265
+ * the series may convert to text by using the ->> operator instead of ->.
266
+ *
267
+ * Returns a tuple of ["Last property name", "Last property type", "Remainder of text"]
268
+ * or the original string input indicating that no opening `->` was found.
269
+ */
270
+ type ParseJsonAccessor < Input extends string > = Input extends `->${infer Remainder } `
271
+ ? Remainder extends `>${infer Remainder } `
272
+ ? ParseIdentifier < Remainder > extends [ infer Name , `${infer Remainder } `]
273
+ ? [ Name , string , EatWhitespace < Remainder > ]
274
+ : ParserError < 'Expected property name after `->>`' >
275
+ : ParseIdentifier < Remainder > extends [ infer Name , `${infer Remainder } `]
276
+ ? ParseJsonAccessor < Remainder > extends [
277
+ infer PropertyName ,
278
+ infer PropertyType ,
279
+ `${infer Remainder } `
280
+ ]
281
+ ? [ PropertyName , PropertyType , EatWhitespace < Remainder > ]
282
+ : [ Name , Json , EatWhitespace < Remainder > ]
283
+ : ParserError < 'Expected property name after `->`' >
284
+ : Input
285
+
242
286
/**
243
287
* Parses an embedded resource, which is an opening `(`, followed by a sequence of
244
288
* nodes, separated by `,`, then a closing `)`.
0 commit comments