Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
67 changes: 56 additions & 11 deletions src/components/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import { promisify } from 'util';
const writeFile = promisify(fs.writeFile);
import TOML from '@iarna/toml';
import { template } from 'lodash';
import { normalizeMethodNameJavascript, normalizeMethodNameRust, extractParameterNames } from './helper';

const tsTemplate = template(`
const tsTemplate = template(
`
// Code generated by @open-rpc/generator DO NOT EDIT.
import {
RequestManager,
Expand Down Expand Up @@ -211,27 +213,70 @@ export class <%= className %> {

<% openrpcDocument.methods.forEach((method) => { %>
/**
* <%= method.summary %>
* <%= method.description %>
*/
// tslint:disable-next-line:max-line-length
public <%= method.name %>: <%= methodTypings.getTypingNames("typescript", method).method %> = (...params) => {
public <%= normalizeMethodNameJavascript(method.name) %>: <%= methodTypings.getTypingNames("typescript", method).method %> = (...params) => {
return this.request("<%= method.name %>", params);
}
<% }); %>
}
export default <%= className %>;
`);
`,
{
imports: {
normalizeMethodNameJavascript,
},
}
);

import Rust from "@json-schema-tools/transpiler/build/codegens/rust";
Rust.prototype.getCodePrefix = function () {
return "";
};

const rsTemplate = template(
`// @generated
// Code generated by @open-rpc/generator DO NOT EDIT.
#![allow(clippy::too_many_arguments)]

const rsTemplate = template(`
#[macro_use]
extern crate jsonrpc_client_core;
use jsonrpsee::core::{client::ClientT, ClientError as Error};

<%= methodTypings.toString("rust", { includeSchemaTypings: true, includeMethodAliasTypings: false }) %>

jsonrpc_client!(pub struct <%= className %> {
<%= methodTypings.toString("rust", { includeSchemaTypings: false, includeMethodAliasTypings: true }) %>
});
`);
pub struct <%= className %><T: ClientT> {
client: T,
}

impl <T: ClientT> <%= className %><T> {

pub fn new(client: T) -> Self {
Self { client }
}

<% openrpcDocument.methods.forEach((method) => { %>
/**
* <%= method.description %>
*/
pub async fn <%= normalizeMethodNameRust(method.name) %>(&self, <%= methodTypings.getParamsTyping("rust", method) %>) -> Result<<%= methodTypings.getTypingNames("rust", method).result %>, Error> {
<% const paramNames = extractParameterNames(methodTypings.getParamsTyping("rust", method)); %>
<% if (paramNames.length > 0) { %>
let mut params: serde_json::Map<String, serde_json::Value> = serde_json::Map::new();
<% paramNames.forEach((paramName, index) => { %>params.insert("<%= paramName %>".to_string(), serde_json::to_value(<%= paramName %>).unwrap());<% }); %>
<% } else { %>let params: serde_json::Map<String, serde_json::Value> = serde_json::Map::new();<% } %>

self.client.request("<%= method.name %>", params).await
}
<% }); %>
}
`,
{
imports: {
normalizeMethodNameRust,
extractParameterNames,
},
}
);

const hooks: IHooks = {
afterCopyStatic: [
Expand Down
4 changes: 2 additions & 2 deletions src/components/docs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as path from 'path';
import { copy, ensureDir, remove } from 'fs-extra';
import { IHooks, IComponent } from './types';
import { IDocsConfig, IDocsExtraConfig } from 'src/config';
import { IDocsExtraConfig } from 'src/config';
import * as fs from 'fs';
import { promisify } from 'util';
import { template, startCase } from 'lodash';
Expand Down Expand Up @@ -198,7 +198,7 @@ const hooks: IHooks = {
],
},
afterCompileTemplate: [
async (dest, frm, component, openrpcDocument): Promise<void> => {
async (dest, frm, component, _): Promise<void> => {
const docsComponent = component as IComponent<IDocsExtraConfig>;
if (!docsComponent.extraConfig) {
return;
Expand Down
104 changes: 104 additions & 0 deletions src/components/helper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { describe, it, expect } from '@jest/globals';
import {
normalizeMethodNameJavascript,
normalizeMethodNameRust,
extractParameterNames,
} from './helper';

describe('Test normalizeMethodNameJavascript', () => {
it('returns the original name if it does not contain a dot', () => {
expect(normalizeMethodNameJavascript('hello')).toBe('hello');
expect(normalizeMethodNameJavascript('foo_bar')).toBe('foo_bar');
});

it('splits and capitalizes correctly with dot', () => {
expect(normalizeMethodNameJavascript('foo.bar')).toBe('fooBar');
expect(normalizeMethodNameJavascript('foo.bar.baz')).toBe('fooBarBaz');
});

it('capitalizes only subsequent parts', () => {
expect(normalizeMethodNameJavascript('one.two.three')).toBe('oneTwoThree');
});

it('handles leading/trailing separators gracefully', () => {
expect(normalizeMethodNameJavascript('.foo.bar')).toBe('FooBar');
expect(normalizeMethodNameJavascript('foo.bar.')).toBe('fooBar');
expect(normalizeMethodNameJavascript('..foo..bar..')).toBe('FooBar');
});

it('handles empty string', () => {
expect(normalizeMethodNameJavascript('')).toBe('');
});
});

describe('Test normalizeMethodNameRust', () => {
it('returns the original name if it does not contain a dot', () => {
expect(normalizeMethodNameRust('hello')).toBe('hello');
expect(normalizeMethodNameRust('foo_bar')).toBe('foo_bar');
});

it('splits and capitalizes correctly with dot', () => {
expect(normalizeMethodNameRust('foo.bar')).toBe('foo_bar');
expect(normalizeMethodNameRust('foo.bar.baz')).toBe('foo_bar_baz');
});

it('capitalizes only subsequent parts', () => {
expect(normalizeMethodNameRust('one.two.three')).toBe('one_two_three');
});

it('handles leading/trailing separators gracefully', () => {
expect(normalizeMethodNameRust('.foo.bar')).toBe('foo_bar');
expect(normalizeMethodNameRust('foo.bar.')).toBe('foo_bar');
expect(normalizeMethodNameRust('..foo..bar..')).toBe('foo_bar');
});

it('handles empty string', () => {
expect(normalizeMethodNameRust('')).toBe('');
});
});

describe('Test extractParameterNames', () => {
it('extracts parameter names from a string with types', () => {
expect(extractParameterNames('number:int, address: string')).toEqual(['number', 'address']);
expect(extractParameterNames('id:number, name:string, active:boolean')).toEqual([
'id',
'name',
'active',
]);
});

it('handles single parameter', () => {
expect(extractParameterNames('value:string')).toEqual(['value']);
});

it('handles parameters without types', () => {
expect(extractParameterNames('param1, param2, param3')).toEqual(['param1', 'param2', 'param3']);
});

it('handles mixed parameters with and without types', () => {
expect(extractParameterNames('id:number, name, active:boolean')).toEqual([
'id',
'name',
'active',
]);
});

it('handles empty string', () => {
expect(extractParameterNames('')).toEqual([]);
});

it('handles string with only whitespace', () => {
expect(extractParameterNames(' ')).toEqual([]);
});

it('handles parameters with extra whitespace', () => {
expect(extractParameterNames(' number : int , address : string ')).toEqual([
'number',
'address',
]);
});

it('filters out empty parameter names', () => {
expect(extractParameterNames('valid:type, , another:type')).toEqual(['valid', 'another']);
});
});
43 changes: 43 additions & 0 deletions src/components/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// This function is used in the template files to normalize method names for JavaScript/TypeScript.
// It removes dots and applies camelCase formatting.
export function normalizeMethodNameJavascript(name: string): string {
// backward compatibility only change on dot inside in name.
if (!name.includes('.')) return name;

const parts = name.split(/[._]/);
return parts
.map((part, index) => (index === 0 ? part : part.charAt(0).toUpperCase() + part.slice(1)))
.join('');
}

// This function is used in the template files to normalize method names for Rust.
// It replace dots with underscores and applies snake_case formatting.
export function normalizeMethodNameRust(name: string): string {
// backward compatibility only change on dot inside in name.
if (!name.includes('.')) return name;

name = name.replace(/\./g, '_');
name = name.toLowerCase();
name = name.replace(/__+/g, '_'); // replace multiple underscores with a single underscore
while (name.startsWith('_')) {
name = name.slice(1); // remove leading underscore
}
while (name.endsWith('_')) {
name = name.slice(0, -1); // remove trailing underscore
}

return name;
}

// Extract parameter names from a parameter string into an array
export function extractParameterNames(paramStr: string): string[] {
if (!paramStr || paramStr.trim() === '') {
return [];
}

return paramStr
.split(',')
.map((param) => param.trim())
.map((param) => param.split(':')[0].trim())
.filter((name) => name.length > 0);
}
1 change: 0 additions & 1 deletion src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { forEach } from 'lodash';
import { OpenRPCDocumentDereferencingError } from '@open-rpc/schema-utils-js';
import { OpenrpcDocument as OpenRPC } from '@open-rpc/meta-schema';
import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
import { isMapIterator } from 'util/types';

const stat = promisify(fs.stat);
const rmdir = promisify(fs.rmdir);
Expand Down
4 changes: 1 addition & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ import { promisify } from 'util';
import { startCase } from 'lodash';
import { OpenrpcDocument as OpenRPC } from '@open-rpc/meta-schema';
import { parseOpenRPCDocument } from '@open-rpc/schema-utils-js';
import { IDocsConfig, TComponentConfig } from './config';
import { TComponentConfig } from './config';
import Typings from '@open-rpc/typings';

import {
defaultClientComponent,
defaultDocComponent,
defaultServerComponent,
IComponentModule,
IHooks,
FHook,
getDefaultComponentTemplatePath,
IComponent,
} from './components';
export * as components from './components';
Expand Down