Skip to content

Commit

Permalink
Fix variant type generation
Browse files Browse the repository at this point in the history
  • Loading branch information
apporc committed Nov 28, 2023
1 parent afb1b12 commit 393ff7c
Show file tree
Hide file tree
Showing 5 changed files with 285 additions and 160 deletions.
92 changes: 32 additions & 60 deletions src/commands/contract/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,34 +34,35 @@ export function getCoreImports(abi: ABI.Def) {

const {type} = findAbiType(field.type, abi)

if (type.includes(' | ')) {
coreImports.push('Variant')
const coreClass = findCoreClassImport(type)

type.split(' | ').forEach((typeString) => {
const coreType = findCoreClassImport(typeString)

if (coreType) {
coreTypes.push(coreType)
}
})
} else {
const coreClass = findCoreClassImport(type)
if (coreClass) {
coreImports.push(coreClass)
}

if (coreClass) {
coreImports.push(coreClass)
}
// We don't need to add action types unless the struct is an action param
if (!structIsActionParams) {
continue
}

// We don't need to add action types unless the struct is an action param
if (!structIsActionParams) {
continue
}
const coreType = findCoreType(type)

const coreType = findCoreType(type)
if (coreType) {
coreTypes.push(coreType)
}
}
}

if (coreType) {
coreTypes.push(coreType)
if (abi.variants.length != 0) {
coreImports.push('Variant')
for (const variant of abi.variants) {
variant.types.forEach((typeString) => {
const {type: abiType} = findAbiType(typeString, abi)
const coreClass = findCoreClassImport(abiType)
if (coreClass) {
coreImports.push(coreClass)
}
}
})
}
}

Expand Down Expand Up @@ -180,6 +181,7 @@ export function findInternalType(
): string {
const {type: typeString, decorator} = findType(type, abi, typeNamespace)

// TODO: inside findType, namespace is prefixed, but format internal is doing the same
return formatInternalType(typeString, typeNamespace, abi, decorator)
}

Expand All @@ -189,7 +191,7 @@ function formatInternalType(
abi: ABI.Def,
decorator = ''
): string {
const structNames = abi.structs.map((struct) => struct.name.toLowerCase())
const structNames = [...abi.structs, ...abi.variants].map((struct) => struct.name.toLowerCase())

let type

Expand All @@ -209,36 +211,10 @@ function findAliasType(typeString: string, abi: ABI.Def): string | undefined {
return alias?.type && `${alias?.type}${decorator || ''}`
}

function findVariantType(
typeString: string,
abi: ABI.Def,
typeNamespace: string,
context: string
): string | undefined {
const abiVariant = abi.variants.find(
(variant) => variant.name.toLowerCase() === typeString.toLowerCase()
)

if (!abiVariant) {
return
}

return abiVariant.types
.map((type) => {
if (context === 'external') {
return parseType(findExternalType(type, typeNamespace, abi))
} else {
return parseType(findInternalType(type, typeNamespace, abi))
}
})
.join(' | ')
}

export function findAbiType(
type: string,
abi: ABI.Def,
typeNamespace = '',
context = 'internal'
typeNamespace = ''
): {type: string; decorator?: string} {
let typeString = parseType(trim(type))

Expand All @@ -252,13 +228,9 @@ export function findAbiType(
typeString = extractDecoratorResponse.type
const decorator = extractDecoratorResponse.decorator

const variantType = findVariantType(typeString, abi, typeNamespace, context)

if (variantType) {
return {type: variantType, decorator}
}

const abiType = abi.structs.find((abiType) => abiType.name === typeString)?.name
const abiType = [...abi.structs, ...abi.variants].find(
(abiType) => abiType.name === typeString
)?.name

if (abiType) {
return {type: `${typeNamespace}${formatClassName(abiType)}`, decorator}
Expand All @@ -268,13 +240,13 @@ export function findAbiType(
}

export function findExternalType(type: string, typeNamespace = '', abi: ABI.Def): string {
const {type: typeString, decorator} = findType(type, abi, typeNamespace, 'external')
const {type: typeString, decorator} = findType(type, abi, typeNamespace)

return `${findCoreType(typeString) || capitalize(typeString)}${decorator === '[]' ? '[]' : ''}`
}

function findType(type: string, abi: ABI.Def, typeNamespace?: string, context = 'internal') {
return findAbiType(type, abi, typeNamespace, context)
function findType(type: string, abi: ABI.Def, typeNamespace?: string) {
return findAbiType(type, abi, typeNamespace)
}

const decorators = ['?', '[]']
Expand Down
135 changes: 112 additions & 23 deletions src/commands/contract/structs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface FieldType {
interface StructData {
structName: string
fields: FieldType[]
variant: boolean
}

interface TypeAlias {
Expand All @@ -27,14 +28,34 @@ export function generateStructClasses(abi) {
const structMembers: ts.ClassDeclaration[] = []

for (const struct of orderedStructs) {
structMembers.push(generateStruct(struct, abi, true))
if (struct.variant) {
structMembers.push(generateVariant(struct, abi, true))
} else {
structMembers.push(generateStruct(struct, abi, true))
}
}

return structMembers
}

export function getActionFieldFromAbi(abi: any): StructData[] {
const structTypes: {structName: string; fields: FieldType[]}[] = []
const structTypes: StructData[] = []

if (abi && abi.variants) {
for (const variant of abi.variants) {
structTypes.push({
structName: variant.name,
fields: variant.types.map((t) => {
return {
name: 'value',
type: t,
optional: false,
}
}),
variant: true,
})
}
}

if (abi && abi.structs) {
for (const struct of abi.structs) {
Expand All @@ -48,13 +69,65 @@ export function getActionFieldFromAbi(abi: any): StructData[] {
})
}

structTypes.push({structName: struct.name, fields})
structTypes.push({structName: struct.name, fields, variant: false})
}
}

return structTypes
}

export function generateVariant(variant, abi: any, isExport = false): ts.ClassDeclaration {
const decoratorArguments: (ts.ObjectLiteralExpression | ts.StringLiteral | ts.Identifier)[] =
variant.fields.map((field) => findVariantStructType(field.type, undefined, abi))

const decorators = [
ts.factory.createDecorator(
ts.factory.createCallExpression(
ts.factory.createIdentifier('Variant.type'),
undefined,
[
ts.factory.createStringLiteral(variant.structName),
ts.factory.createArrayLiteralExpression(decoratorArguments),
]
)
),
]

const valueField = ts.factory.createPropertyDeclaration(
[],
ts.factory.createIdentifier(field.name),

Check failure on line 98 in src/commands/contract/structs.ts

View workflow job for this annotation

GitHub Actions / Node.js v18

Cannot find name 'field'.
ts.factory.createToken(ts.SyntaxKind.ExclamationToken),
ts.factory.createUnionTypeNode(
variant.fields.map((field) => {
return ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier(
findFieldStructTypeString(field.type, undefined, abi)
),
undefined
)
})
),
undefined
)

return ts.factory.createClassDeclaration(
isExport
? [...decorators, ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)]
: decorators,
ts.factory.createIdentifier(formatClassName(variant.structName)),
undefined, // typeParameters
[
ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [
ts.factory.createExpressionWithTypeArguments(
ts.factory.createIdentifier('Struct'),
[]
),
]),
], // heritageClauses
[valueField]
)
}

export function generateStruct(struct, abi, isExport = false): ts.ClassDeclaration {
const decorators = [
ts.factory.createDecorator(
Expand Down Expand Up @@ -139,24 +212,11 @@ export function generateField(
),
]

let typeReferenceNode: ts.TypeReferenceNode | ts.UnionTypeNode

const structTypeString = findFieldStructTypeString(field.type, namespace, abi)

if (structTypeString.includes(' | ')) {
typeReferenceNode = ts.factory.createUnionTypeNode(
structTypeString.split(' | ').map((type) => {
return ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier(type),
undefined
)
})
)
} else {
typeReferenceNode = ts.factory.createTypeReferenceNode(
extractDecorator(structTypeString).type
)
}
const typeReferenceNode = ts.factory.createTypeReferenceNode(
extractDecorator(structTypeString).type
)

let typeNode: ts.TypeNode

Expand Down Expand Up @@ -229,6 +289,39 @@ function findDependencies(
return dependencies
}

function findVariantStructType(
typeString: string,
namespace: string | undefined,
abi: ABI.Def
): ts.Identifier | ts.StringLiteral | ts.ObjectLiteralExpression {
const variantTypeString = findFieldStructTypeString(typeString, namespace, abi)

if (['string', 'string[]', 'boolean', 'boolean[]'].includes(variantTypeString.toLowerCase())) {
return ts.factory.createStringLiteral(formatFieldString(variantTypeString))
}

const isArray = variantTypeString.endsWith('[]')
if (isArray) {
const optionsProps: ts.ObjectLiteralElementLike[] = []
optionsProps.push(
ts.factory.createPropertyAssignment(
ts.factory.createIdentifier('type'),
ts.factory.createIdentifier(extractDecorator(variantTypeString).type)
)
)
optionsProps.push(
ts.factory.createPropertyAssignment(
ts.factory.createIdentifier('array'),
ts.factory.createTrue()
)
)

const optionsObject = ts.factory.createObjectLiteralExpression(optionsProps)
return optionsObject
} else {
return ts.factory.createIdentifier(variantTypeString)
}
}
function findFieldStructType(
typeString: string,
namespace: string | undefined,
Expand All @@ -238,10 +331,6 @@ function findFieldStructType(
findFieldStructTypeString(typeString, namespace, abi)
).type

if (fieldTypeString.includes(' | ')) {
return ts.factory.createIdentifier('Variant')
}

if (['string', 'string[]', 'boolean', 'boolean[]'].includes(fieldTypeString.toLowerCase())) {
return ts.factory.createStringLiteral(formatFieldString(fieldTypeString))
}
Expand Down
Loading

0 comments on commit 393ff7c

Please sign in to comment.