1- import { ProtoEnum , ProtoMessage } from "@proto-graphql/proto-descriptors" ;
1+ import { ProtoEnum , ProtoField , ProtoMessage , ProtoScalarType } from "@proto-graphql/proto-descriptors" ;
2+ import { camelCase } from "change-case" ;
23import * as path from "path" ;
34import { code , Code , imp } from "ts-poet" ;
45import {
56 EnumType ,
7+ InputObjectField ,
68 InputObjectType ,
79 InterfaceType ,
10+ ObjectField ,
11+ ObjectOneofField ,
812 ObjectType ,
913 OneofUnionType ,
1014 PrinterOptions ,
@@ -30,6 +34,24 @@ export function filename(
3034 }
3135}
3236
37+ export function generatedGraphQLTypeImportPath (
38+ field :
39+ | ObjectField < ObjectType | EnumType | InterfaceType | SquashedOneofUnionType >
40+ | InputObjectField < InputObjectType | EnumType >
41+ | ObjectOneofField ,
42+ opts : PrinterOptions
43+ ) : string | null {
44+ if ( field instanceof ObjectOneofField ) return null ;
45+ const [ fromPath , toPath ] = [ filename ( field . parent , opts ) , filename ( field . type , opts ) ] . map ( ( f ) =>
46+ path . isAbsolute ( f ) ? `.${ path . sep } ${ f } ` : f
47+ ) ;
48+
49+ if ( fromPath === toPath ) return null ;
50+
51+ const importPath = path . relative ( path . dirname ( fromPath ) , toPath ) . replace ( / \. t s $ / , "" ) ;
52+ return importPath . match ( / ^ [ \. \/ ] / ) ? importPath : `./${ importPath } ` ;
53+ }
54+
3355/** Remove nullish values recursively. */
3456export function compact ( v : any ) : any {
3557 if ( typeof v !== "object" ) return v ;
@@ -46,19 +68,69 @@ function compactObj<In extends Out, Out extends Record<string, unknown>>(obj: In
4668 } , { } as Out ) ;
4769}
4870
49- export function protoType ( origProto : ProtoMessage | ProtoEnum , opts : PrinterOptions ) : Code {
71+ export function protoType ( origProto : ProtoMessage | ProtoEnum | ProtoField , opts : PrinterOptions ) : Code {
72+ const origProtoType = origProto . kind === "Field" ? origProto . type : origProto ;
73+ if ( origProtoType . kind === "Scalar" ) {
74+ throw new Error ( "cannot import protobuf primitive types" ) ;
75+ }
76+ let proto = origProtoType ;
77+ const chunks = [ proto . name ] ;
78+ while ( proto . parent . kind !== "File" ) {
79+ proto = proto . parent ;
80+ chunks . unshift ( proto . name ) ;
81+ }
82+ switch ( opts . protobuf ) {
83+ case "google-protobuf" : {
84+ return code `${ imp ( `${ chunks [ 0 ] } @${ protoImportPath ( proto , opts ) } ` ) } ${ chunks
85+ . slice ( 1 )
86+ . map ( ( c ) => `.${ c } ` )
87+ . join ( "" ) } `;
88+ }
89+ case "protobufjs" : {
90+ chunks . unshift ( ...proto . file . package . split ( "." ) ) ;
91+ const importPath = protoImportPath ( origProto . kind === "Field" ? origProto . parent : origProto , opts ) ;
92+ return code `${ imp ( `${ chunks [ 0 ] } @${ importPath } ` ) } .${ chunks . slice ( 1 ) . join ( "." ) } ` ;
93+ }
94+ case "ts-proto" : {
95+ return code `${ imp ( `${ chunks . join ( "_" ) } @${ protoImportPath ( proto , opts ) } ` ) } ` ;
96+ }
97+ /* istanbul ignore next */
98+ default : {
99+ const _exhaustiveCheck : never = opts ;
100+ throw "unreachable" ;
101+ }
102+ }
103+ }
104+
105+ export function createGetFieldValueCode ( parent : Code , proto : ProtoField , opts : PrinterOptions ) : Code {
106+ switch ( opts . protobuf ) {
107+ case "google-protobuf" : {
108+ return code `${ parent } .${ googleProtobufFieldAccessor ( "get" , proto ) } ()` ;
109+ }
110+ case "protobufjs" : {
111+ return code `${ parent } .${ camelCase ( proto . name ) } ` ;
112+ }
113+ case "ts-proto" : {
114+ return code `${ parent } .${ proto . jsonName } ` ;
115+ }
116+ /* istanbul ignore next */
117+ default : {
118+ const _exhaustiveCheck : never = opts ;
119+ throw "unreachable" ;
120+ }
121+ }
122+ }
123+
124+ export function createSetFieldValueCode ( parent : Code , value : Code , proto : ProtoField , opts : PrinterOptions ) : Code {
50125 switch ( opts . protobuf ) {
51- case "google-protobuf" :
52- case "protobufjs" :
53- throw new Error ( `not implemented: ${ opts . protobuf } ` ) ;
126+ case "google-protobuf" : {
127+ return code `${ parent } .${ googleProtobufFieldAccessor ( "set" , proto ) } (${ value } )` ;
128+ }
129+ case "protobufjs" : {
130+ return code `${ parent } .${ camelCase ( proto . name ) } = ${ value } ` ;
131+ }
54132 case "ts-proto" : {
55- let proto = origProto ;
56- let name = proto . name ;
57- while ( proto . parent . kind !== "File" ) {
58- proto = proto . parent ;
59- name = `${ proto . name } _${ name } ` ;
60- }
61- return code `${ imp ( `${ name } @${ protoImportPath ( proto , opts ) } ` ) } ` ;
133+ return code `${ parent } .${ proto . jsonName } = ${ value } ` ;
62134 }
63135 /* istanbul ignore next */
64136 default : {
@@ -67,3 +139,38 @@ export function protoType(origProto: ProtoMessage | ProtoEnum, opts: PrinterOpti
67139 }
68140 }
69141}
142+
143+ function googleProtobufFieldAccessor ( type : "get" | "set" , proto : ProtoField ) {
144+ return `${ type } ${ upperCaseFirst ( proto . jsonName ) } ${ proto . list ? "List" : "" } ` ;
145+ }
146+
147+ function upperCaseFirst ( s : string ) : string {
148+ return `${ s . charAt ( 0 ) . toUpperCase ( ) } ${ s . slice ( 1 ) } ` ;
149+ }
150+
151+ const longScalarPrimitiveTypes : ReadonlySet < ProtoScalarType > = new Set ( [
152+ "int64" ,
153+ "uint64" ,
154+ "fixed64" ,
155+ "sfixed64" ,
156+ "sint64" ,
157+ ] ) ;
158+ const longScalarWrapperTypes : ReadonlySet < string > = new Set ( [
159+ "google.protobuf.Int64Value" ,
160+ "google.protobuf.UInt64Value" ,
161+ ] ) ;
162+
163+ export function isProtobufLong ( proto : ProtoField ) : boolean {
164+ switch ( proto . type . kind ) {
165+ case "Scalar" :
166+ return longScalarPrimitiveTypes . has ( proto . type . type ) ;
167+ case "Message" :
168+ return longScalarWrapperTypes . has ( proto . type . fullName . toString ( ) ) ;
169+ default :
170+ return false ;
171+ }
172+ }
173+
174+ export function isWellKnownType ( proto : ProtoField [ "type" ] ) : proto is ProtoMessage {
175+ return proto . kind === "Message" && proto . file . name . startsWith ( "google/protobuf/" ) ;
176+ }
0 commit comments