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 230b02b
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 144 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
123 changes: 102 additions & 21 deletions src/commands/contract/structs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export function generateStructClasses(abi) {
structMembers.push(generateStruct(struct, abi, true))
}

return structMembers
const variants: ts.ClassDeclaration[] = generateVariants(abi, true)

return [...variants, ...structMembers]
}

export function getActionFieldFromAbi(abi: any): StructData[] {
Expand All @@ -55,6 +57,69 @@ export function getActionFieldFromAbi(abi: any): StructData[] {
return structTypes
}

export function generateVariants(abi: any, isExport = false): ts.ClassDeclaration[] {
const variants: ts.ClassDeclaration[] = []
for (const abiVariant of abi.variants) {
const decoratorArguments: (
| ts.ObjectLiteralExpression
| ts.StringLiteral
| ts.Identifier
)[] = abiVariant.types.map((typeString) =>
findVariantStructType(typeString, undefined, abi)
)

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

const valueField = ts.factory.createPropertyDeclaration(
[],
ts.factory.createIdentifier('value'),
ts.factory.createToken(ts.SyntaxKind.ExclamationToken),
ts.factory.createUnionTypeNode(
abiVariant.types.map((type) => {
return ts.factory.createTypeReferenceNode(
ts.factory.createIdentifier(
findFieldStructTypeString(type, undefined, abi)
),
undefined
)
})
),
undefined
)

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

export function generateStruct(struct, abi, isExport = false): ts.ClassDeclaration {
const decorators = [
ts.factory.createDecorator(
Expand Down Expand Up @@ -139,24 +204,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 +281,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 +323,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 230b02b

Please sign in to comment.