import * as _ from 'lodash'; import { AbiTypes, DocAgnosticFormat, DocSection, DoxityAbiDoc, DoxityContractObj, DoxityDocObj, DoxityInput, EventArg, Parameter, Property, SolidityMethod, Type, TypeDocTypes, } from '../types'; export const doxityUtils = { convertToDocAgnosticFormat(doxityDocObj: DoxityDocObj): DocAgnosticFormat { const docAgnosticFormat: DocAgnosticFormat = {}; _.each(doxityDocObj, (doxityContractObj: DoxityContractObj, contractName: string) => { const doxityConstructor = _.find(doxityContractObj.abiDocs, (abiDoc: DoxityAbiDoc) => { return abiDoc.type === AbiTypes.Constructor; }); const constructors = []; if (!_.isUndefined(doxityConstructor)) { const constructor = { isConstructor: true, name: doxityContractObj.name, comment: doxityConstructor.details, returnComment: doxityConstructor.return, callPath: '', parameters: doxityUtils._convertParameters(doxityConstructor.inputs), returnType: doxityUtils._convertType(doxityContractObj.name), }; constructors.push(constructor); } const doxityMethods: DoxityAbiDoc[] = _.filter( doxityContractObj.abiDocs, (abiDoc: DoxityAbiDoc) => { return doxityUtils._isMethod(abiDoc); }, ); const methods: SolidityMethod[] = _.map( doxityMethods, (doxityMethod: DoxityAbiDoc) => { const outputs = !_.isUndefined(doxityMethod.outputs) ? doxityMethod.outputs : []; let returnTypeIfExists: Type; if (outputs.length === 0) { // no-op. It's already undefined } else if (outputs.length === 1) { const outputsType = outputs[0].type; returnTypeIfExists = doxityUtils._convertType(outputsType); } else { const outputsType = `[${_.map(outputs, output => output.type).join(', ')}]`; returnTypeIfExists = doxityUtils._convertType(outputsType); } // For ZRXToken, we want to convert it to zrxToken, rather then simply zRXToken const callPath = contractName !== 'ZRXToken' ? `${contractName[0].toLowerCase()}${contractName.slice(1)}.` : `${contractName.slice(0, 3).toLowerCase()}${contractName.slice(3)}.`; const method = { isConstructor: false, isConstant: doxityMethod.constant, isPayable: doxityMethod.payable, name: doxityMethod.name, comment: doxityMethod.details, returnComment: doxityMethod.return, callPath, parameters: doxityUtils._convertParameters(doxityMethod.inputs), returnType: returnTypeIfExists, }; return method; }, ); const doxityProperties: DoxityAbiDoc[] = _.filter( doxityContractObj.abiDocs, (abiDoc: DoxityAbiDoc) => { return doxityUtils._isProperty(abiDoc); }, ); const properties = _.map(doxityProperties, (doxityProperty: DoxityAbiDoc) => { // We assume that none of our functions return more then a single return value let typeName = doxityProperty.outputs[0].type; if (!_.isEmpty(doxityProperty.inputs)) { // Properties never have more then a single input typeName = `(${doxityProperty.inputs[0].type} => ${typeName})`; } const property = { name: doxityProperty.name, type: doxityUtils._convertType(typeName), comment: doxityProperty.details, }; return property; }); const doxityEvents = _.filter( doxityContractObj.abiDocs, (abiDoc: DoxityAbiDoc) => abiDoc.type === AbiTypes.Event, ); const events = _.map(doxityEvents, doxityEvent => { const event = { name: doxityEvent.name, eventArgs: doxityUtils._convertEventArgs(doxityEvent.inputs), }; return event; }); const docSection: DocSection = { comment: doxityContractObj.title, constructors, methods, properties, types: [], functions: [], events, }; docAgnosticFormat[contractName] = docSection; }); return docAgnosticFormat; }, _convertParameters(inputs: DoxityInput[]): Parameter[] { const parameters = _.map(inputs, input => { const parameter = { name: input.name, comment: input.description, isOptional: false, type: doxityUtils._convertType(input.type), }; return parameter; }); return parameters; }, _convertType(typeName: string): Type { const type = { name: typeName, typeDocType: TypeDocTypes.Intrinsic, }; return type; }, _isMethod(abiDoc: DoxityAbiDoc): boolean { if (abiDoc.type !== AbiTypes.Function) { return false; } const hasInputs = !_.isEmpty(abiDoc.inputs); const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name); const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase(); const isMethod = hasNamedOutputIfExists && !isNameAllCaps; return isMethod; }, _isProperty(abiDoc: DoxityAbiDoc): boolean { if (abiDoc.type !== AbiTypes.Function) { return false; } const hasInputs = !_.isEmpty(abiDoc.inputs); const hasNamedOutputIfExists = !hasInputs || !_.isEmpty(abiDoc.inputs[0].name); const isNameAllCaps = abiDoc.name === abiDoc.name.toUpperCase(); const isProperty = !hasNamedOutputIfExists || isNameAllCaps; return isProperty; }, _convertEventArgs(inputs: DoxityInput[]): EventArg[] { const eventArgs = _.map(inputs, input => { const eventArg = { isIndexed: input.indexed, name: input.name, type: doxityUtils._convertType(input.type), }; return eventArg; }); return eventArgs; }, };