'use strict'
// Compile with solcjs
const fs = require('fs')
const path = require('path')
const assert = require('chai').assert
const { List, Map, Set }
= require('immutable')
const { traverseDirs, COMPILES_DIR, getImmutableKey, setImmutableKey, Logger,
awaitInputter, awaitOutputter }
= require('demo-utils')
const LOGGER = new Logger('ContractsManager')
const cm = {}
/**
* A ContractsManager which can read and clean compiled contracts.
* @param _outputter {Function} a (possibly asynchronous) function that
* takes (key: string, val: {Map} | {List} | null ) and returns a Promise or
* other value that you want returned from `compile` or `clean*` methods.
* If missing, _outputter defaults to `setImmutableKey`
* to a local file-based DB store.
*/
cm.ContractsManager = class {
constructor({sourcePathList, inputter, outputter}) {
this.sourcePathSet = Set(sourcePathList)
.filter((d) => {
if (!fs.existsSync(d)) {
LOGGER.warn(`Source path ${d} does not exist, ignoring.`)
return false
} else {
return true
}
})
assert((inputter && outputter) || (!inputter && !outputter))
this.inputter = inputter || getImmutableKey
this.outputter = outputter || setImmutableKey
}
async getContracts() {
if (!this.contracts) {
const contractSources = []
traverseDirs(
this.sourcePathSet.toJS(),
(fnParts) => { return (fnParts.length > 1 && !fnParts[1].startsWith('sol')) },
function(source, f) {
const fn = List(f.split(path.sep)).last()
const fb = path.basename(fn.split('.')[0])
contractSources.push(fb)
LOGGER.debug(`Source ${fb}`)
}
)
this.contracts = await awaitInputter(
this.inputter(COMPILES_DIR, new Map({})),
(contractOutputs) => {
return {
contractSources: List(contractSources),
contractOutputs: contractOutputs
}
}
)
}
return this.contracts
}
/**
* Asynchronously return a contract previously outputted at key `/compiles/`
* @param contractName name of the compiled contract
*/
async getContract(contractName) {
const { contractOutputs } = await this.getContracts()
return contractOutputs.get(contractName)
}
async setContract(contractName, contractOutput) {
const compileKey = `${COMPILES_DIR}/${contractName}`
this.contracts.contractOutputs =
this.contracts.contractOutputs.set(contractName, contractOutput)
return awaitOutputter(
this.outputter(compileKey, contractOutput, true),
() => { return contractOutput }
)
}
async cleanContract(contractName) {
this.contracts.contractOutputs =
this.contracts.contractOutputs.set(contractName, null)
return this.outputter(`${COMPILES_DIR}/${contractName}`, null)
}
async cleanAllCompiles() {
if (this.contracts) {
this.contracts.contractOutputs = Map({})
}
return this.outputter(`${COMPILES_DIR}`, null)
}
async cleanCompile(compile) {
return Promise.all(List(
compile.map((compile, compileName) => {
return this.cleanContract(compileName)
}).values()).toJS()
)
}
}
/**
* @return true if the given object is a compile output from a Compiler, otherwise false
*/
cm.isCompile = (_compile) => {
return (_compile && Map.isMap(_compile) && _compile.count() > 0 &&
_compile.reduce((prev, val) => {
return prev && val.get('type') === 'compile'
}, true))
}
/**
* @return true if the given object is a compile output retrieved from db, otherwise false
*/
cm.isContract = (_contract) => {
return (Map.isMap(_contract) && _contract.get('type') === 'compile')
}
module.exports = cm