Clean-up scripts. (#6832)
This commit is contained in:
parent
3a01068747
commit
aa929fe6ae
@ -24,15 +24,12 @@
|
||||
"build": "npm run build:lib && npm run build:app",
|
||||
"build:app": "webpack --progress --config webpack/app",
|
||||
"build:lib": "webpack --progress --config webpack/libraries",
|
||||
"build:markdown": "babel-node ./scripts/build-rpc-markdown.js",
|
||||
"build:json": "babel-node ./scripts/build-rpc-json.js",
|
||||
"build:embed": "EMBED=1 node webpack/embed",
|
||||
"build:i18n": "npm run clean && npm run build && babel-node ./scripts/build-i18n.js",
|
||||
"ci:build": "npm run ci:build:lib && npm run ci:build:app && npm run ci:build:embed",
|
||||
"ci:build:app": "NODE_ENV=production webpack --progress --config webpack/app",
|
||||
"ci:build:lib": "NODE_ENV=production webpack --progress --config webpack/libraries",
|
||||
"ci:build:npm": "NODE_ENV=production webpack --progress --config webpack/npm",
|
||||
"ci:build:jsonrpc": "babel-node ./scripts/build-rpc-json.js --output .npmjs/jsonrpc",
|
||||
"ci:build:embed": "NODE_ENV=production EMBED=1 node webpack/embed",
|
||||
"clean": "rm -rf ./.build ./.coverage ./.happypack ./build ./node_modules/.cache",
|
||||
"coveralls": "npm run testCoverage && coveralls < coverage/lcov.info",
|
||||
|
@ -1,70 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import yargs from 'yargs';
|
||||
|
||||
import interfaces from '../src/jsonrpc';
|
||||
|
||||
const argv = yargs.default('output', 'release').argv;
|
||||
|
||||
const INDEX_JSON = path.join(__dirname, `../${argv.output}/index.json`);
|
||||
const methods = [];
|
||||
|
||||
function formatDescription (obj) {
|
||||
const optional = obj.optional ? '(optional) ' : '';
|
||||
const defaults = obj.default ? `(default: ${obj.default}) ` : '';
|
||||
|
||||
return `${obj.type.name} - ${optional}${defaults}${obj.desc}`;
|
||||
}
|
||||
|
||||
function formatType (obj) {
|
||||
if (obj.type === Object && obj.details) {
|
||||
const formatted = {};
|
||||
|
||||
Object.keys(obj.details).sort().forEach((key) => {
|
||||
formatted[key] = formatType(obj.details[key]);
|
||||
});
|
||||
|
||||
return {
|
||||
desc: formatDescription(obj),
|
||||
details: formatted
|
||||
};
|
||||
} else if (obj.type && obj.type.name) {
|
||||
return formatDescription(obj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object.keys(interfaces).sort().forEach((group) => {
|
||||
Object.keys(interfaces[group]).sort().forEach((name) => {
|
||||
const method = interfaces[group][name];
|
||||
const deprecated = method.deprecated ? ' (Deprecated and not supported, to be removed in a future version)' : '';
|
||||
|
||||
methods.push({
|
||||
name: `${group}_${name}`,
|
||||
desc: `${method.desc}${deprecated}`,
|
||||
params: method.params.map(formatType),
|
||||
returns: formatType(method.returns),
|
||||
inputFormatters: method.params.map((param) => param.format || null),
|
||||
outputFormatter: method.returns.format || null
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
fs.writeFileSync(INDEX_JSON, JSON.stringify({ methods: methods }, null, 2), 'utf8');
|
@ -1,328 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { isPlainObject } from 'lodash';
|
||||
|
||||
import { info, warn, error } from './helpers/log';
|
||||
import { Dummy } from '../src/jsonrpc/helpers';
|
||||
import interfaces from '../src/jsonrpc';
|
||||
import rustMethods from './helpers/parsed-rpc-traits';
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, '../docs');
|
||||
|
||||
if (!fs.existsSync(ROOT_DIR)) {
|
||||
fs.mkdirSync(ROOT_DIR);
|
||||
}
|
||||
|
||||
Object.keys(rustMethods).forEach((group) => {
|
||||
Object.keys(rustMethods[group]).forEach((method) => {
|
||||
if (interfaces[group] == null || interfaces[group][method] == null) {
|
||||
error(`${group}_${method} is defined in Rust traits, but not in js/src/jsonrpc/interfaces`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function printType (type, obj) {
|
||||
if (!type) {
|
||||
throw new Error(`Invalid type in ${JSON.stringify(obj)}`);
|
||||
}
|
||||
|
||||
return type.print || `\`${type.name}\``;
|
||||
}
|
||||
|
||||
function formatDescription (obj, prefix = '', indent = '') {
|
||||
const optional = obj.optional ? '(optional) ' : '';
|
||||
const defaults = obj.default ? `(default: \`${obj.default}\`) ` : '';
|
||||
|
||||
return `${indent}${prefix}${printType(obj.type, obj)} - ${optional}${defaults}${obj.desc}`;
|
||||
}
|
||||
|
||||
function formatType (obj) {
|
||||
if (obj == null || obj.type == null) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
const details = obj.details || obj.type.details;
|
||||
|
||||
if (details) {
|
||||
const sub = Object.keys(details).map((key) => {
|
||||
return formatDescription(details[key], `\`${key}\`: `, ' - ');
|
||||
}).join('\n');
|
||||
|
||||
return `${formatDescription(obj)}\n${sub}`;
|
||||
} else if (obj.type && obj.type.name) {
|
||||
return formatDescription(obj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
const rpcReqTemplate = {
|
||||
method: 'web3_clientVersion',
|
||||
params: [],
|
||||
id: 1,
|
||||
jsonrpc: '2.0'
|
||||
};
|
||||
|
||||
const { isDummy } = Dummy;
|
||||
const { isArray } = Array;
|
||||
|
||||
// Checks if a field definition has an example,
|
||||
// or describes an object with fields that recursively have examples of their own,
|
||||
// or is optional.
|
||||
function hasExample ({ optional, example, details } = {}) {
|
||||
if (optional || example !== undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (details !== undefined) {
|
||||
const values = Object.keys(details).map((key) => details[key]);
|
||||
|
||||
return values.every(hasExample);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove all optional (trailing) params without examples from an array
|
||||
function removeOptionalWithoutExamples (arr) {
|
||||
return arr.filter(({ optional, example, details }) => {
|
||||
return !optional || example !== undefined || details !== undefined;
|
||||
});
|
||||
}
|
||||
|
||||
// Grabs JSON compatible
|
||||
function getExample (obj) {
|
||||
if (isArray(obj)) {
|
||||
return removeOptionalWithoutExamples(obj).map(getExample);
|
||||
}
|
||||
|
||||
const { example, details } = obj;
|
||||
|
||||
if (example === undefined && details !== undefined) {
|
||||
const nested = {};
|
||||
|
||||
Object.keys(details).forEach((key) => {
|
||||
nested[key] = getExample(details[key]);
|
||||
});
|
||||
|
||||
return nested;
|
||||
}
|
||||
|
||||
return example;
|
||||
}
|
||||
|
||||
function stringifyExample (example, dent = '') {
|
||||
const indent = `${dent} `;
|
||||
|
||||
if (isDummy(example)) {
|
||||
return example.toString();
|
||||
}
|
||||
|
||||
if (isArray(example)) {
|
||||
const last = example.length - 1;
|
||||
|
||||
// If all elements are dummies, print out a single line.
|
||||
// Also covers empty arrays.
|
||||
if (example.every(isDummy)) {
|
||||
const dummies = example.map(d => d.toString());
|
||||
|
||||
return `[${dummies.join(', ')}]`;
|
||||
}
|
||||
|
||||
// For arrays containing just one object or string, don't unwind the array to multiline
|
||||
if (last === 0 && (isPlainObject(example[0]) || typeof example[0] === 'string')) {
|
||||
return `[${stringifyExample(example[0], dent)}]`;
|
||||
}
|
||||
|
||||
const elements = example.map((value, index) => {
|
||||
const comma = index !== last ? ',' : '';
|
||||
const comment = value != null && value._comment ? ` // ${value._comment}` : '';
|
||||
|
||||
return `${stringifyExample(value, indent)}${comma}${comment}`;
|
||||
});
|
||||
|
||||
return `[\n${indent}${elements.join(`\n${indent}`)}\n${dent}]`;
|
||||
}
|
||||
|
||||
if (isPlainObject(example)) {
|
||||
const keys = Object.keys(example);
|
||||
const last = keys.length - 1;
|
||||
|
||||
// print out an empty object
|
||||
if (last === -1) {
|
||||
return '{}';
|
||||
}
|
||||
|
||||
const elements = keys.map((key, index) => {
|
||||
const value = example[key];
|
||||
const comma = index !== last ? ',' : '';
|
||||
const comment = value && value._comment ? ` // ${example[key]._comment}` : '';
|
||||
|
||||
return `${JSON.stringify(key)}: ${stringifyExample(value, indent)}${comma}${comment}`;
|
||||
});
|
||||
|
||||
return `{\n${indent}${elements.join(`\n${indent}`)}\n${dent}}`;
|
||||
}
|
||||
|
||||
return JSON.stringify(example);
|
||||
}
|
||||
|
||||
function buildExample (name, method) {
|
||||
// deprecated, don't care
|
||||
if (method.deprecated) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const logPostfix = method.subdoc ? ` (${method.subdoc})` : '';
|
||||
|
||||
const hasReqExample = method.params.every(hasExample);
|
||||
const hasResExample = hasExample(method.returns);
|
||||
|
||||
if (!hasReqExample && !hasResExample) {
|
||||
error(`${name} has no examples${logPostfix}`);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
const examples = [];
|
||||
|
||||
if (hasReqExample) {
|
||||
const params = getExample(method.params);
|
||||
const req = Dummy.stringifyJSON(Object.assign({}, rpcReqTemplate, { method: name, params }));
|
||||
|
||||
examples.push(`Request\n\`\`\`bash\ncurl --data '${req}' -H "Content-Type: application/json" -X POST localhost:8545\n\`\`\``);
|
||||
} else {
|
||||
warn(`${name} has a response example but not a request example${logPostfix}`);
|
||||
}
|
||||
|
||||
if (hasResExample) {
|
||||
const res = stringifyExample({
|
||||
id: 1,
|
||||
jsonrpc: '2.0',
|
||||
result: getExample(method.returns)
|
||||
});
|
||||
|
||||
examples.push(`Response\n\`\`\`js\n${res}\n\`\`\``);
|
||||
} else {
|
||||
if (typeof method.returns === 'string') {
|
||||
info(`${name} has a request example and only text description for response${logPostfix}`);
|
||||
} else {
|
||||
warn(`${name} has a request example but not a response example${logPostfix}`);
|
||||
}
|
||||
}
|
||||
|
||||
return `\n\n#### Example\n\n${examples.join('\n\n')}`;
|
||||
}
|
||||
|
||||
function buildParameters (params) {
|
||||
if (params.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let md = `0. ${params.map(formatType).join('\n0. ')}`;
|
||||
|
||||
if (params.length > 0 && params.every(hasExample) && !isDummy(params[0].example)) {
|
||||
const example = getExample(params);
|
||||
|
||||
md = `${md}\n\n\`\`\`js\nparams: ${stringifyExample(example)}\n\`\`\``;
|
||||
}
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
Object.keys(interfaces).sort().forEach((group) => {
|
||||
const spec = interfaces[group];
|
||||
|
||||
for (const key in spec) {
|
||||
const method = spec[key];
|
||||
|
||||
if (!method || !method.subdoc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const subgroup = `${group}_${method.subdoc}`;
|
||||
|
||||
interfaces[subgroup] = interfaces[subgroup] || {};
|
||||
|
||||
interfaces[subgroup][key] = method;
|
||||
delete spec[key];
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(interfaces).sort().forEach((group) => {
|
||||
let preamble = `# The \`${group}\` Module`;
|
||||
let markdown = `## JSON-RPC methods\n`;
|
||||
|
||||
const spec = interfaces[group];
|
||||
|
||||
if (spec._preamble) {
|
||||
preamble = `${preamble}\n\n${spec._preamble}`;
|
||||
}
|
||||
|
||||
const content = [];
|
||||
const tocMain = [];
|
||||
const tocSections = {};
|
||||
|
||||
// Comparator that will sort by sections first, names second
|
||||
function methodComparator (a, b) {
|
||||
const sectionA = spec[a].section || '';
|
||||
const sectionB = spec[b].section || '';
|
||||
|
||||
return sectionA.localeCompare(sectionB) || a.localeCompare(b);
|
||||
}
|
||||
|
||||
Object.keys(spec).sort(methodComparator).forEach((iname) => {
|
||||
const method = spec[iname];
|
||||
const groupName = group.replace(/_.*$/, '');
|
||||
const name = `${groupName}_${iname}`;
|
||||
|
||||
if (method.nodoc || method.deprecated) {
|
||||
info(`Skipping ${name}: ${method.nodoc || 'Deprecated'}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (rustMethods[groupName] == null || rustMethods[groupName][iname] == null) {
|
||||
error(`${name} is defined in js/src/jsonrpc/interfaces, but not in Rust traits`);
|
||||
}
|
||||
|
||||
const desc = method.desc;
|
||||
const params = buildParameters(method.params);
|
||||
const returns = `- ${formatType(method.returns)}`;
|
||||
const example = buildExample(name, method);
|
||||
|
||||
const { section } = method;
|
||||
const toc = section ? tocSections[section] = tocSections[section] || [] : tocMain;
|
||||
|
||||
toc.push(`- [${name}](#${name.toLowerCase()})`);
|
||||
content.push(`### ${name}\n\n${desc}\n\n#### Parameters\n\n${params || 'None'}\n\n#### Returns\n\n${returns || 'None'}${example}`);
|
||||
});
|
||||
|
||||
markdown = `${markdown}\n${tocMain.join('\n')}`;
|
||||
|
||||
Object.keys(tocSections).sort().forEach((section) => {
|
||||
markdown = `${markdown}\n\n#### ${section}\n${tocSections[section].join('\n')}`;
|
||||
});
|
||||
|
||||
markdown = `${markdown}\n\n## JSON-RPC API Reference\n\n${content.join('\n\n***\n\n')}\n\n`;
|
||||
|
||||
const mdFile = path.join(ROOT_DIR, `${group}.md`);
|
||||
|
||||
fs.writeFileSync(mdFile, `${preamble}\n\n${markdown}`, 'utf8');
|
||||
});
|
@ -2,17 +2,12 @@
|
||||
set -e
|
||||
|
||||
# variables
|
||||
PACKAGES=( "parity" "etherscan" "shapeshift" "jsonrpc" )
|
||||
PACKAGES=( "parity" )
|
||||
|
||||
# change into the build directory
|
||||
BASEDIR=`dirname $0`
|
||||
cd $BASEDIR/..
|
||||
|
||||
# build jsonrpc
|
||||
echo "*** Building JSONRPC .json"
|
||||
mkdir -p .npmjs/jsonrpc
|
||||
npm run ci:build:jsonrpc
|
||||
|
||||
# build all packages
|
||||
echo "*** Building packages for npmjs"
|
||||
echo "$NPM_TOKEN" >> ~/.npmrc
|
||||
|
@ -1,32 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import chalk from 'chalk';
|
||||
|
||||
// INFO Logging helper
|
||||
export function info (log) {
|
||||
console.log(chalk.blue(`INFO:\t${log}`));
|
||||
}
|
||||
|
||||
// WARN Logging helper
|
||||
export function warn (log) {
|
||||
console.warn(chalk.yellow(`WARN:\t${log}`));
|
||||
}
|
||||
|
||||
// ERROR Logging helper
|
||||
export function error (log) {
|
||||
console.error(chalk.red(`ERROR:\t${log}`));
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
// ```js
|
||||
// rustMethods['eth']['call'] === true
|
||||
// ```
|
||||
const rustMethods = {};
|
||||
|
||||
export default rustMethods;
|
||||
|
||||
// Get a list of JSON-RPC from Rust trait source code
|
||||
function parseMethodsFromRust (source) {
|
||||
// Matching the custom `rpc` attribute with it's doc comment
|
||||
const attributePattern = /((?:\s*\/\/\/.*$)*)\s*#\[rpc\(([^)]+)\)]/gm;
|
||||
const commentPattern = /\s*\/\/\/\s*/g;
|
||||
const separatorPattern = /\s*,\s*/g;
|
||||
const assignPattern = /([\S]+)\s*=\s*"([^"]*)"/;
|
||||
const ignorePattern = /@(ignore|deprecated|unimplemented|alias)\b/i;
|
||||
|
||||
const methods = [];
|
||||
|
||||
source.toString().replace(attributePattern, (match, comment, props) => {
|
||||
comment = comment.replace(commentPattern, '\n').trim();
|
||||
|
||||
// Skip deprecated methods
|
||||
if (ignorePattern.test(comment)) {
|
||||
return match;
|
||||
}
|
||||
|
||||
props.split(separatorPattern).forEach((prop) => {
|
||||
const [, key, value] = prop.split(assignPattern) || [];
|
||||
|
||||
if (key === 'name' && value != null) {
|
||||
methods.push(value);
|
||||
}
|
||||
});
|
||||
|
||||
return match;
|
||||
});
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
// Get a list of all JSON-RPC methods from all defined traits
|
||||
function getMethodsFromRustTraits () {
|
||||
const traitsDir = path.join(__dirname, '../../../rpc/src/v1/traits');
|
||||
|
||||
return fs.readdirSync(traitsDir)
|
||||
.filter((name) => name !== 'mod.rs' && /\.rs$/.test(name))
|
||||
.map((name) => fs.readFileSync(path.join(traitsDir, name)))
|
||||
.map(parseMethodsFromRust)
|
||||
.reduce((a, b) => a.concat(b));
|
||||
}
|
||||
|
||||
getMethodsFromRustTraits().sort().forEach((method) => {
|
||||
const [group, name] = method.split('_');
|
||||
|
||||
// Skip methods with malformed names
|
||||
if (group == null || name == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
rustMethods[group] = rustMethods[group] || {};
|
||||
rustMethods[group][name] = true;
|
||||
});
|
@ -3,7 +3,7 @@ set -e
|
||||
|
||||
# variables
|
||||
UTCDATE=`date -u "+%Y%m%d-%H%M%S"`
|
||||
PACKAGES=( "parity" "etherscan" "shapeshift" "jsonrpc" )
|
||||
PACKAGES=( "parity" )
|
||||
BRANCH=$CI_BUILD_REF_NAME
|
||||
GIT_JS_PRECOMPILED="https://${GITHUB_JS_PRECOMPILED}:@github.com/paritytech/js-precompiled.git"
|
||||
GIT_PARITY="https://${GITHUB_JS_PRECOMPILED}:@github.com/paritytech/parity.git"
|
||||
@ -67,11 +67,6 @@ if [ "$BRANCH" == "master" ]; then
|
||||
echo "*** Building packages for npmjs"
|
||||
echo "$NPM_TOKEN" >> ~/.npmrc
|
||||
|
||||
# build jsonrpc
|
||||
echo "*** Building JSONRPC .json"
|
||||
mkdir -p .npmjs/jsonrpc
|
||||
npm run ci:build:jsonrpc
|
||||
|
||||
for PACKAGE in ${PACKAGES[@]}
|
||||
do
|
||||
echo "*** Building $PACKAGE"
|
||||
|
@ -1,254 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Rust/Parity ABI struct autogenerator.
|
||||
// By Gav Wood, 2016.
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
String.prototype.replaceAll = function(f, t) { return this.split(f).join(t); }
|
||||
String.prototype.toSnake = function(){
|
||||
return this.replace(/([A-Z])/g, function($1){return "_"+$1.toLowerCase();});
|
||||
};
|
||||
|
||||
function makeContractFile(name, json, prefs) {
|
||||
return `// Autogenerated from JSON contract definition using Rust contract convertor.
|
||||
// Command line: ${process.argv.slice(2).join(' ')}
|
||||
#![allow(unused_imports)]
|
||||
use std::string::String;
|
||||
use std::result::Result;
|
||||
use std::fmt;
|
||||
use {util, ethabi};
|
||||
|
||||
${convertContract(name, json, prefs)}
|
||||
`;
|
||||
}
|
||||
|
||||
function convertContract(name, json, prefs) {
|
||||
return `${prefs._pub ? "pub " : ""}struct ${name} {
|
||||
contract: ethabi::Contract,
|
||||
pub address: util::Address,
|
||||
${prefs._explicit_do_call ? "" : `do_call: Box<Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send${prefs._sync ? " + Sync " : ""}+ 'static>,`}
|
||||
}
|
||||
impl ${name} {
|
||||
pub fn new${prefs._explicit_do_call ? "" : "<F>"}(address: util::Address${prefs._explicit_do_call ? "" : `, do_call: F`}) -> Self
|
||||
${prefs._explicit_do_call ? "" : `where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send ${prefs._sync ? "+ Sync " : ""}+ 'static`} {
|
||||
${name} {
|
||||
contract: ethabi::Contract::new(ethabi::Interface::load(b"${JSON.stringify(json.filter(a => a.type == 'function')).replaceAll('"', '\\"')}").expect("JSON is autogenerated; qed")),
|
||||
address: address,
|
||||
${prefs._explicit_do_call ? "" : `do_call: Box::new(do_call),`}
|
||||
}
|
||||
}
|
||||
fn as_string<T: fmt::Debug>(e: T) -> String { format!("{:?}", e) }
|
||||
${json.filter(x => x.type == 'function').map(x => convertFunction(x, prefs)).join("\n")}
|
||||
}`;
|
||||
}
|
||||
|
||||
function mapType(name, type, _prefs) {
|
||||
let prefs = _prefs || {};
|
||||
var m;
|
||||
if ((m = type.match(/^bytes(\d+)$/)) != null && m[1] <= 32) {
|
||||
if (prefs['string'])
|
||||
return `&str`;
|
||||
else
|
||||
return `&util::H${m[1] * 8}`;
|
||||
}
|
||||
if ((m = type.match(/^(u?)int(\d+)$/)) != null) {
|
||||
var n = [8, 16, 32, 64, 128, 160, 256].filter(i => m[2] <= i)[0];
|
||||
if (n) {
|
||||
if (n <= 64)
|
||||
return `${m[1] == 'u' ? 'u' : 'i'}${n}`;
|
||||
if (m[1] == 'u')
|
||||
return `util::U${n}`;
|
||||
// ERROR - unsupported integer (signed > 32 or unsigned > 256)
|
||||
}
|
||||
}
|
||||
if (type == "address")
|
||||
return "&util::Address";
|
||||
if (type == "bool")
|
||||
return "bool";
|
||||
if (type == "string")
|
||||
return "&str";
|
||||
if (type == "bytes")
|
||||
return "&[u8]";
|
||||
|
||||
console.log(`Unsupported argument type: ${type} (${name})`);
|
||||
}
|
||||
|
||||
function mapReturnType(name, type, _prefs) {
|
||||
let prefs = _prefs || {};
|
||||
var m;
|
||||
if ((m = type.match(/^bytes(\d+)$/)) != null && m[1] <= 32) {
|
||||
if (prefs['string'])
|
||||
return `String`;
|
||||
else
|
||||
return `util::H${m[1] * 8}`;
|
||||
}
|
||||
if ((m = type.match(/^(u?)int(\d+)$/)) != null) {
|
||||
var n = [8, 16, 32, 64, 128, 160, 256].filter(i => m[2] <= i)[0];
|
||||
if (n) {
|
||||
if (n <= 64)
|
||||
return `${m[1] == 'u' ? 'u' : 'i'}${n}`;
|
||||
if (m[1] == 'u')
|
||||
return `util::U${n}`;
|
||||
// ERROR - unsupported integer (signed > 32 or unsigned > 256)
|
||||
}
|
||||
}
|
||||
if (type == "address")
|
||||
return "util::Address";
|
||||
if (type == "bool")
|
||||
return "bool";
|
||||
if (type == "string")
|
||||
return "String";
|
||||
if (type == "bytes")
|
||||
return "Vec<u8>";
|
||||
if (type == "address[]")
|
||||
return "Vec<util::Address>";
|
||||
|
||||
console.log(`Unsupported return type: ${type} (${name})`);
|
||||
}
|
||||
|
||||
function convertToken(name, type, _prefs) {
|
||||
let prefs = _prefs || {};
|
||||
var m;
|
||||
if ((m = type.match(/^bytes(\d+)$/)) != null && m[1] <= 32) {
|
||||
if (prefs['string'])
|
||||
return `ethabi::Token::FixedBytes(${name}.as_bytes().to_owned())`;
|
||||
else
|
||||
return `ethabi::Token::FixedBytes(${name}.as_ref().to_owned())`;
|
||||
}
|
||||
if ((m = type.match(/^(u?)int(\d+)$/)) != null) {
|
||||
var n = [8, 16, 32, 64, 128, 160, 256].filter(i => m[2] <= i)[0];
|
||||
if (n) {
|
||||
if (m[1] == 'u')
|
||||
return `ethabi::Token::Uint({ let mut r = [0u8; 32]; ${n <= 64 ? "util::U256::from(" + name + " as u64)" : name}.to_big_endian(&mut r); r })`;
|
||||
else if (n <= 32)
|
||||
return `ethabi::Token::Int(pad_i32(${name} as i32))`;
|
||||
// ERROR - unsupported integer (signed > 32 or unsigned > 256)
|
||||
}
|
||||
}
|
||||
if (type == "address")
|
||||
return `ethabi::Token::Address(${name}.clone().0)`;
|
||||
if (type == "bool")
|
||||
return `ethabi::Token::Bool(${name})`;
|
||||
if (type == "string")
|
||||
return `ethabi::Token::String(${name}.to_owned())`;
|
||||
if (type == "bytes")
|
||||
return `ethabi::Token::Bytes(${name}.to_owned())`;
|
||||
|
||||
console.log(`Unsupported argument type: ${type} (${name})`);
|
||||
}
|
||||
|
||||
function tokenType(name, type, _prefs) {
|
||||
let prefs = _prefs || {};
|
||||
var m;
|
||||
if ((m = type.match(/^bytes(\d+)$/)) != null && m[1] <= 32)
|
||||
return `${name}.to_fixed_bytes()`;
|
||||
if ((m = type.match(/^(u?)int(\d+)$/)) != null) {
|
||||
return `${name}.to_${m[1]}int()`;
|
||||
}
|
||||
if (type == "address")
|
||||
return `${name}.to_address()`;
|
||||
if (type == "bool")
|
||||
return `${name}.to_bool()`;
|
||||
if (type == "string")
|
||||
return `${name}.to_string()`;
|
||||
if (type == "bytes")
|
||||
return `${name}.to_bytes()`;
|
||||
if (type == "address[]")
|
||||
return `${name}.to_array().and_then(|v| v.into_iter().map(|a| a.to_address()).collect::<Option<Vec<[u8; 20]>>>())`;
|
||||
|
||||
console.log(`Unsupported return type: ${type} (${name})`);
|
||||
}
|
||||
|
||||
function tokenCoerce(name, type, _prefs) {
|
||||
let prefs = _prefs || {};
|
||||
var m;
|
||||
if ((m = type.match(/^bytes(\d+)$/)) != null && m[1] <= 32) {
|
||||
if (prefs['string'])
|
||||
return `String::from_utf8(${name}).unwrap_or_else(String::new)`;
|
||||
else
|
||||
return `util::H${m[1] * 8}::from_slice(${name}.as_ref())`;
|
||||
}
|
||||
if ((m = type.match(/^(u?)int(\d+)$/)) != null) {
|
||||
var n = [8, 16, 32, 64, 128, 160, 256].filter(i => m[2] <= i)[0];
|
||||
if (n && m[1] == 'u')
|
||||
return `util::U${n <= 64 ? 256 : n}::from(${name}.as_ref())` + (n <= 64 ? `.as_u64() as u${n}` : '');
|
||||
// ERROR - unsupported integer (signed or unsigned > 256)
|
||||
}
|
||||
if (type == "address")
|
||||
return `util::Address::from(${name})`;
|
||||
if (type == "bool")
|
||||
return `${name}`;
|
||||
if (type == "string")
|
||||
return `${name}`;
|
||||
if (type == "bytes")
|
||||
return `${name}`;
|
||||
if (type == "address[]")
|
||||
return `${name}.into_iter().map(|a| util::Address::from(a)).collect::<Vec<_>>()`;
|
||||
|
||||
console.log(`Unsupported return type: ${type} (${name})`);
|
||||
}
|
||||
|
||||
function tokenExtract(expr, type, _prefs) {
|
||||
return `{ let r = ${expr}; let r = ${tokenType('r', type, _prefs)}.ok_or("Invalid type returned")?; ${tokenCoerce('r', type, _prefs)} }`;
|
||||
}
|
||||
|
||||
function convertFunction(json, _prefs) {
|
||||
let cprefs = _prefs || {};
|
||||
let prefs = (_prefs || {})[json.name] || (_prefs || {})['_'] || {};
|
||||
let snakeName = json.name.toSnake();
|
||||
let params = json.inputs.map((x, i) => (x.name ? x.name.toSnake() : ("_" + (i + 1))) + ": " + mapType(x.name, x.type, prefs[x.name]));
|
||||
let returns = json.outputs.length != 1 ? "(" + json.outputs.map(x => mapReturnType(x.name, x.type, prefs[x.name])).join(", ") + ")" : mapReturnType(json.outputs[0].name, json.outputs[0].type, prefs[json.outputs[0].name]);
|
||||
return `
|
||||
/// Auto-generated from: \`${JSON.stringify(json)}\`
|
||||
#[allow(dead_code)]
|
||||
pub fn ${snakeName}${cprefs._explicit_do_call ? "<F>" : ""}(&self${cprefs._explicit_do_call ? `, do_call: &F` : ""}${params.length > 0 ? ', ' + params.join(", ") : ''}) -> Result<${returns}, String>
|
||||
${cprefs._explicit_do_call ? `where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send ${prefs._sync ? "+ Sync " : ""}` : ""} {
|
||||
let call = self.contract.function("${json.name}".into()).map_err(Self::as_string)?;
|
||||
let data = call.encode_call(
|
||||
vec![${json.inputs.map((x, i) => convertToken(x.name ? x.name.toSnake() : ("_" + (i + 1)), x.type, prefs[x.name])).join(', ')}]
|
||||
).map_err(Self::as_string)?;
|
||||
${json.outputs.length > 0 ? 'let output = ' : ''}call.decode_output((${cprefs._explicit_do_call ? "" : "self."}do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
|
||||
${json.outputs.length > 0 ? 'let mut result = output.into_iter().rev().collect::<Vec<_>>();' : ''}
|
||||
Ok((${json.outputs.map((o, i) => tokenExtract('result.pop().ok_or("Invalid return arity")?', o.type, prefs[o.name])).join(', ')}))
|
||||
}`;
|
||||
}
|
||||
|
||||
// default preferences:
|
||||
let prefs = {"_pub": true, "_": {"_client": {"string": true}, "_platform": {"string": true}}, "_sync": true};
|
||||
// default contract json ABI
|
||||
let jsonabi = [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}];
|
||||
// default name
|
||||
let name = 'Contract';
|
||||
|
||||
// parse command line options
|
||||
for (let i = 1; i < process.argv.length; ++i) {
|
||||
let arg = process.argv[i];
|
||||
if (arg.indexOf("--jsonabi=") == 0) {
|
||||
jsonabi = arg.slice(10);
|
||||
if (fs.existsSync(jsonabi)) {
|
||||
jsonabi = JSON.parse(fs.readFileSync(jsonabi).toString());
|
||||
}
|
||||
} else if (arg.indexOf("--explicit-do-call") == 0) {
|
||||
prefs._explicit_do_call = true;
|
||||
} else if (arg.indexOf("--name=") == 0) {
|
||||
name = arg.slice(7);
|
||||
}
|
||||
}
|
||||
|
||||
let out = makeContractFile(name, jsonabi, prefs);
|
||||
console.log(`${out}`);
|
@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
ll
|
||||
ls
|
||||
la
|
||||
echo "list of biniries"
|
||||
exit
|
@ -1,16 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
RUSTFMT="rustfmt --write-mode overwrite"
|
||||
|
||||
$RUSTFMT ./ethash/src/lib.rs
|
||||
$RUSTFMT ./ethcore/src/lib.rs
|
||||
$RUSTFMT ./evmjit/src/lib.rs
|
||||
$RUSTFMT ./json/src/lib.rs
|
||||
$RUSTFMT ./miner/src/lib.rs
|
||||
$RUSTFMT ./parity/main.rs
|
||||
$RUSTFMT ./rpc/src/lib.rs
|
||||
$RUSTFMT ./signer/src/lib.rs
|
||||
$RUSTFMT ./dapps/src/lib.rs
|
||||
$RUSTFMT ./sync/src/lib.rs
|
||||
$RUSTFMT ./util/src/lib.rs
|
||||
|
Loading…
Reference in New Issue
Block a user