Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Perform a sync between Rust and JS when generating markdown instead of in spec tests #4408

Merged
merged 3 commits into from
Feb 2, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 15 additions & 16 deletions js/scripts/build-rpc-markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,26 @@

import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
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);
}

// INFO Logging helper
function info (log) {
console.log(chalk.blue(`INFO:\t${log}`));
}

// WARN Logging helper
function warn (log) {
console.warn(chalk.yellow(`WARN:\t${log}`));
}

// ERROR Logging helper
function error (log) {
console.error(chalk.red(`ERROR:\t${log}`));
}
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) {
return type.print || `\`${type.name}\``;
Expand Down Expand Up @@ -291,14 +285,19 @@ Object.keys(interfaces).sort().forEach((group) => {

Object.keys(spec).sort(methodComparator).forEach((iname) => {
const method = spec[iname];
const name = `${group.replace(/_.*$/, '')}_${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)}`;
Expand Down
16 changes: 16 additions & 0 deletions js/scripts/helpers/log.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import chalk from 'chalk';
Copy link
Contributor

@jacogr jacogr Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just add the GPL file header here.


// 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}`));
}
65 changes: 65 additions & 0 deletions js/scripts/helpers/parsed-rpc-traits.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import fs from 'fs';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, GPL header.

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;
});
80 changes: 0 additions & 80 deletions js/src/jsonrpc/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
// 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 interfaces from './';
import * as customTypes from './types';

Expand All @@ -27,91 +25,13 @@ function verifyType (obj) {
}
}

// 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));
}

const rustMethods = {};

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;
});

describe('jsonrpc/interfaces', () => {
describe('Rust trait methods', () => {
Object.keys(rustMethods).forEach((group) => {
describe(group, () => {
Object.keys(rustMethods[group]).forEach((name) => {
describe(name, () => {
it('has a defined JS interface', () => {
expect(interfaces[group][name]).to.exist;
});
});
});
});
});
});

Object.keys(interfaces).forEach((group) => {
describe(group, () => {
Object.keys(interfaces[group]).forEach((name) => {
const method = interfaces[group][name];

describe(name, () => {
if (!method.nodoc) {
it('is present in Rust codebase', () => {
expect(rustMethods[group][name]).to.exist;
});
}

it('has the correct interface', () => {
expect(method.desc).to.be.a('string');
expect(method.params).to.be.an('array');
Expand Down