mazzaroth-cli
Version:
Tool that wraps the mazzaroth-js node/contract clients and facilitates interaction with a Mazzaroth node.
153 lines (135 loc) • 4.73 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _nearley = require('nearley');
var _nearley2 = _interopRequireDefault(_nearley);
var _grammar = require('./grammar/grammar.js');
var _grammar2 = _interopRequireDefault(_grammar);
var _readline = require('readline');
var _readline2 = _interopRequireDefault(_readline);
var _debug = require('debug');
var _debug2 = _interopRequireDefault(_debug);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* This file encapsulates the basic reading and parsing of terminal input for
* interactive contract-cli. It is fairly straight forward, and simply pulls
* lines from stdin, parses them with a simple grammar, executes the correct
* logic and prints the results to stdout.
*/
const debug = (0, _debug2.default)('mazzaroth-cli:contract-io');
/**
* Helper function for formatting an abiEntry and printing logging it with
* console.log.
*
* @param abiEntry
*
* @return none
*/
function outputAbiFunc(abiEntry) {
let output = ` ${abiEntry.name}(`;
const types = abiEntry.inputs.map(x => x.parameterType);
output += types.join(', ');
output += ')';
if (abiEntry.outputs[0]) {
output += ` -> ${abiEntry.outputs[0].parameterType}`;
}
console.log(output);
}
/**
* After constructing, you can call 'run' on a ContractIO object, which will
* drop the user into an interactive CLI and only return when they exit the CLI.
*
* Wraps the readline library in some Mazzaroth specific logic to accomplish
* this.
*/
class ContractIO {
/**
* The interactive contract CLI requires a contract client to work. This
* constructor also creates an instance of the readline interface.
*
* @param contractClient The client used to make contract calls based on the
* user input from readline.
*/
constructor(contractClient) {
debug('constructed contract IO with contractClient: %o', contractClient);
this.rl = _readline2.default.createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'Mazz> '
});
this.contractClient = contractClient;
}
/**
* Helper function that simply goes through all the functions in the abiJSON
* provided by the contractClient and outputs them. This is called when the
* user types 'abi' into the CLI to output what functions are available to
* call.
*/
abi() {
const functions = this.contractClient.abiJson.functions.filter(x => x.functionType === 'function');
const readonlys = this.contractClient.abiJson.functions.filter(x => x.functionType === 'readonly');
console.log();
console.log('Functions: ');
functions.forEach(outputAbiFunc);
console.log();
console.log('ReadOnly Functions: ');
readonlys.forEach(outputAbiFunc);
console.log();
this.rl.prompt();
}
/**
* Executes a parsed contract function using the contract client, then logs
* the result and prompts for the next line.
*
* @param functionName Name of the contract function to call.
* @param args Arguments to the contract function in JSON format.
*
* @return none
*/
executeContractFunction(txExpiration, functionName, args) {
if (!this.contractClient[functionName]) {
throw new Error(`${functionName} is not a contract function`);
}
this.contractClient[functionName](txExpiration, ...args).then(res => {
console.log(res);
this.rl.prompt();
}).catch(e => {
console.log(e);
this.rl.prompt();
});
}
/**
* Called to initiate the interactive CLI for the user. Repeatedly calls
* readline prompt parsing the results. First checks if the result is a single
* command that exists on this class (for example abi). If so it executes that
* function. Otherwise it is assumed that it is a properly parsed function
* call then executed on the contract client.
*/
run() {
this.rl.prompt();
this.rl.on('line', line => {
try {
const res = new _nearley2.default.Parser(_nearley2.default.Grammar.fromCompiled(_grammar2.default)).feed(line);
if (res.results.length) {
if (this[res.results[0]]) {
this[res.results[0]]();
} else {
this.executeContractFunction(res.results[0].txExpiration, res.results[0].name, res.results[0].args);
}
} else {
console.log(`Incomplete statement: "${line}"`);
this.rl.prompt();
}
} catch (e) {
console.log(e);
this.rl.prompt();
}
}).on('close', () => {
console.log('');
console.log('peace bro~');
process.exit(0);
});
}
}
exports.default = ContractIO;