@@ -38,14 +38,17 @@ type Digit = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0'
3838
3939type Letter = Alphabet | Digit | '_'
4040
41+ type Json = string | number | boolean | null | { [ key : string ] : Json } | Json [ ]
42+
4143// /**
4244// * Parsed node types.
4345// * Currently only `*` and all other fields.
4446// */
4547// type ParsedNode =
4648// | { star: true }
4749// | { name: string; original: string }
48- // | { name: string; foreignTable: true };
50+ // | { name: string; foreignTable: true }
51+ // | { name: string; type: T };
4952
5053/**
5154 * Parser errors.
@@ -90,6 +93,8 @@ type ConstructFieldDefinition<
9093 }
9194 : Field extends { name : string ; original : string }
9295 ? { [ K in Field [ 'name' ] ] : Row [ Field [ 'original' ] ] }
96+ : Field extends { name : string ; type : infer T }
97+ ? { [ K in Field [ 'name' ] ] : T }
9398 : Record < string , unknown >
9499
95100/**
@@ -131,17 +136,19 @@ type ParseIdentifier<Input extends string> = ReadLetters<Input>
131136 * A node is one of the following:
132137 * - `*`
133138 * - `field`
139+ * - `field->json...`
134140 * - `field(nodes)`
135141 * - `field!hint(nodes)`
136142 * - `field!inner(nodes)`
137143 * - `field!hint!inner(nodes)`
138144 * - `renamed_field:field`
145+ * - `renamed_field:field->json...`
139146 * - `renamed_field:field(nodes)`
140147 * - `renamed_field:field!hint(nodes)`
141148 * - `renamed_field:field!inner(nodes)`
142149 * - `renamed_field:field!hint!inner(nodes)`
143150 *
144- * TODO: casting operators `::text`, JSON operators `->`, `->>`.
151+ * TODO: casting operators `::text`, more support for JSON operators `->`, `->>`.
145152 */
146153type ParseNode < Input extends string > = Input extends ''
147154 ? ParserError < 'Empty string' >
@@ -225,6 +232,13 @@ type ParseNode<Input extends string> = Input extends ''
225232 ]
226233 ? // `renamed_field:field(nodes)`
227234 [ { 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 > ]
228242 : ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
229243 ? ParseEmbeddedResource < EatWhitespace < Remainder > >
230244 : // `renamed_field:field`
@@ -233,12 +247,42 @@ type ParseNode<Input extends string> = Input extends ''
233247 : ParseEmbeddedResource < EatWhitespace < Remainder > > extends [ infer Fields , `${infer Remainder } `]
234248 ? // `field(nodes)`
235249 [ { 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 > ]
236257 : ParseEmbeddedResource < EatWhitespace < Remainder > > extends ParserError < string >
237258 ? ParseEmbeddedResource < EatWhitespace < Remainder > >
238259 : // `field`
239260 [ { name : Name ; original : Name } , EatWhitespace < Remainder > ]
240261 : ParserError < `Expected identifier at \`${Input } \``>
241262
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+
242286/**
243287 * Parses an embedded resource, which is an opening `(`, followed by a sequence of
244288 * nodes, separated by `,`, then a closing `)`.
0 commit comments