import { TypeAssertion , PrimitiveTypeAssertion , PrimitiveValueTypeAssertion , RepeatedAssertion , SpreadAssertion , SequenceAssertion , OneOfAssertion , OptionalAssertion , EnumAssertion , ObjectAssertion , TypeAssertionMap , CodegenContext } from '../../types' ; import { escapeString } from '../../lib/escape' ; function formatTypeName ( ty , ctx , typeName ) { if ( typeName . includes ( '.' ) ) { return generateProto3CodeInner ( ty , false , ctx ) ; } return typeName ; } function formatProto3CodeDocComment ( ty , nestLevel ) { let code = '' ; const indent = '    ' . repeat ( nestLevel ) ; const docComment = typeof ty === 'string' ? ty : ty . docComment ; if ( docComment ) { if ( 0 <= docComment . indexOf ( '\n' ) ) { code += `${ indent }/**\n${ indent }  ${ docComment . split ( '\n' ) . map ( x => x . trimLeft ( ) ) . join ( `\n${ indent } ` ) }\n${ indent } */\n` ; } else { code += `${ indent }/** ${ docComment } */\n` ; } } return code ; } function formatMemberType ( ty , ctx ) { if ( ty . typeName ) { return formatTypeName ( ty , ctx , ty . typeName ) ; } else { switch ( ty . kind ) { case 'primitive' : return generateProto3CodePrimitive ( ty , ctx ) ; case 'primitive-value' : return generateProto3CodePrimitiveValue ( ty , ctx ) ; case 'repeated' : return generateProto3CodeRepeated ( ty , ctx ) ; case 'one-of' : return generateProto3CodeOneOf ( ty , ctx ) ; default : return 'object' ; } } } function appendOptionalModifier ( name ) { switch ( name ) { case 'double' : return 'google.protobuf.DoubleValue' ; case 'int64' : return 'google.protobuf.Int64Value' ; case 'int32' : return 'google.protobuf.Int32Value' ; case 'string' : return 'google.protobuf.StringValue' ; case 'bool' : return 'google.protobuf.BoolValue' ; default : return name ; } } function isNullableOneOf ( ty , ctx ) { const filtered = ty . oneOf . filter ( x => ! ( x . kind === 'primitive' && ( x . primitiveName === 'null' || x . primitiveName === 'undefined' ) || x . kind === 'primitive-value' && ( x . value === null || x . value === void 0 ) ) ) ; return ( filtered . length === 1 && ty . oneOf . length !== 1 ? filtered [ 0 ] : null ) ; } function generateProto3CodePrimitive ( ty , ctx ) { switch ( ty . primitiveName ) { case 'number' : return 'double' ; case 'integer' : return 'int32' ; case 'bigint' : return 'string' ; case 'string' : return 'string' ; case 'boolean' : return 'bool' ; case 'undefined' : case 'null' : default : return 'google.protobuf.Any' ; } } function generateProto3CodePrimitiveValue ( ty , ctx ) { if ( ty . value === null ) { return 'google.protobuf.Any' ; } if ( ty . value === void 0 ) { return 'google.protobuf.Any' ; } switch ( typeof ty . value ) { case 'number' : return 'double' ; case 'bigint' : return 'string' ; case 'string' : return 'string' ; case 'boolean' : return 'bool' ; default : return 'google.protobuf.Any' ; } } function generateProto3CodeRepeated ( ty , ctx ) { return ( `repeated ${ ty . repeated . typeName ? formatTypeName ( ty . repeated , ctx , ty . repeated . typeName ) : ty . repeated . kind === 'repeated' ? 'google.protobuf.Any' : generateProto3CodeInner ( ty . repeated , false , ctx ) }` ) ; } function generateProto3CodeSpread ( ty , ctx ) { return '' ; } function generateProto3CodeSequence ( ty , ctx ) { return 'repeated google.protobuf.Any' ; } function generateProto3CodeOneOf ( ty , ctx ) { const z = isNullableOneOf ( ty , ctx ) ; if ( z ) { return appendOptionalModifier ( formatMemberType ( z , ctx ) ) ; } else { return 'google.protobuf.Any' ; } } function generateProto3CodeOptional ( ty , ctx ) { return appendOptionalModifier ( generateProto3CodeInner ( ty . optional , false , ctx ) ) ; } function generateProto3CodeEnum ( ty , ctx ) { return ( ty . typeName ? formatTypeName ( ty , ctx , ty . typeName ) : 'google.protobuf.Any' ) ; } function generateProto3CodeObject ( ty , isInterface , ctx ) { if ( ty . members . length === 0 ) { return '{}' ; } const sep = isInterface ? ';\n' : ',\n' ; let count = 1 ; const memberLines = ty . members . map ( x => `${ formatProto3CodeDocComment ( x [ 3 ] || '' , ctx . nestLevel + 1 ) }${ '    ' . repeat ( ctx . nestLevel + 1 ) }${ x [ 1 ] . typeName ? formatTypeName ( x [ 1 ] , { ... ctx , nestLevel : ctx . nestLevel + 1 } , x [ 1 ] . typeName ) : generateProto3CodeInner ( x [ 1 ] , false , { ... ctx , nestLevel : ctx . nestLevel + 1 } ) } ${ x [ 0 ] } = ${ count ++ }` ) ; return ( `{\n${ memberLines . join ( sep ) }${ sep }${ '    ' . repeat ( ctx . nestLevel ) }}` ) ; } function generateProto3CodeInner ( ty , isInterface , ctx ) { switch ( ty . kind ) { case 'never' : case 'any' : case 'unknown' : return 'google.protobuf.Any' ; case 'primitive' : return generateProto3CodePrimitive ( ty , ctx ) ; case 'primitive-value' : return generateProto3CodePrimitiveValue ( ty , ctx ) ; case 'repeated' : return generateProto3CodeRepeated ( ty , ctx ) ; case 'spread' : return generateProto3CodeSpread ( ty , ctx ) ; case 'sequence' : return generateProto3CodeSequence ( ty , ctx ) ; case 'one-of' : return generateProto3CodeOneOf ( ty , ctx ) ; case 'optional' : return generateProto3CodeOptional ( ty , ctx ) ; case 'enum' : return generateProto3CodeEnum ( ty , ctx ) ; case 'object' : return generateProto3CodeObject ( ty , isInterface , ctx ) ; case 'symlink' : return ty . symlinkTargetName ; case 'operator' : throw new Error ( `Unexpected type assertion: ${ ( ty as any ) . kind }` ) ; default : throw new Error ( `Unknown type assertion: ${ ( ty as any ) . kind }` ) ; } } export function generateProto3Code ( types ) { let code = `
syntax = "proto3";
import "google/protobuf/wrappers.proto";
import "google/protobuf/any.proto";

` ; const ctx = { nestLevel : 0 } ; for ( const ty of types . entries ( ) ) { if ( ty [ 1 ] . ty . noOutput ) { const indent0 = '    ' . repeat ( ctx . nestLevel ) ; const indent1 = '    ' . repeat ( ctx . nestLevel + 1 ) ; code += `message ${ ty [ 0 ] } {\n${ indent1 }google.protobuf.Any value = 1;\n${ indent0 }}\n\n` ; continue ; } code += formatProto3CodeDocComment ( ty [ 1 ] . ty , ctx . nestLevel ) ; if ( ty [ 1 ] . ty . kind === 'object' ) { code += `message ${ ty [ 0 ] } ${ generateProto3CodeInner ( ty [ 1 ] . ty , true , ctx ) }\n\n` ; } else if ( ty [ 1 ] . ty . kind === 'enum' ) { const indent0 = '    ' . repeat ( ctx . nestLevel ) ; const indent1 = '    ' . repeat ( ctx . nestLevel + 1 ) ; if ( 0 < ty [ 1 ] . ty . values . filter ( x => typeof x [ 1 ] !== 'number' ) . length ) { code += `message ${ ty [ 0 ] } {\n${ indent1 }google.protobuf.Any value = 1;\n${ indent0 }}\n\n` ; } else { code += `enum ${ ty [ 0 ] } {\n${ indent1 }option allow_alias = true;\n${ ty [ 1 ] . ty . values . filter ( x => x [ 1 ] === 0 ) . length === 0 ? `${ indent1 }${ ty [ 0 ] }__UNKNOWN__ = 0;\n` : '' }${ ty [ 1 ] . ty . values . map ( x => `${ formatProto3CodeDocComment ( x [ 2 ] || '' , ctx . nestLevel + 1 ) }${ indent1 }${ ( ( ) => { if ( typeof x [ 1 ] === 'number' ) { return `${ ty [ 0 ] }_${ x [ 0 ] } = ${ x [ 1 ] }` ; } else { return `${ ty [ 0 ] }_${ x [ 0 ] } = '${ escapeString ( x [ 1 ] ) }'` ; } } ) ( ) };\n` ) . join ( '' ) }${ indent0 }}\n\n` ; } } else if ( ty [ 1 ] . ty . kind === 'never' && ty [ 1 ] . ty . passThruCodeBlock ) { } else { const indent0 = '    ' . repeat ( ctx . nestLevel ) ; const indent1 = '    ' . repeat ( ctx . nestLevel + 1 ) ; code += `message ${ ty [ 0 ] } {\n${ indent1 }${ generateProto3CodeInner ( ty [ 1 ] . ty , false , ctx ) } value = 1;\n${ indent0 }}\n\n` ; } } return code ; }