Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added js implementation of nui rpc. #109

Merged
merged 1 commit into from
May 31, 2024
Merged
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
7 changes: 7 additions & 0 deletions nui/include/nui/backend/rpc_hub.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ namespace Nui
callRemoteImpl(name);
}

// alias for callRemote
template <typename... Args>
void call(std::string const& name, Args&&... args) const
{
callRemote(name, std::forward<Args>(args)...);
}

/**
* @brief Enables file dialog functionality
*/
Expand Down
28 changes: 28 additions & 0 deletions nui/include/nui/frontend/rpc_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,34 @@ namespace Nui
return RemoteCallable{std::move(name)};
}

/**
* @brief Get a callable remote function and call it immediately.
*
* @param name Name of the function.
* @param args Arguments to pass to the function.
* @return auto The result of the function.
*/
template <typename... ArgsT>
static auto call(std::string name, ArgsT&&... args)
{
return getRemoteCallable(std::move(name))(std::forward<ArgsT>(args)...);
}

/**
* @brief Get a callable remote function and call it immediately with a callback.
*
* @param name Name of the function.
* @param cb The callback function.
* @param args Arguments to pass to the function.
* @return auto The result of the function.
*/
template <typename FunctionT, typename... ArgsT>
static auto callWithBackChannel(std::string name, FunctionT&& cb, ArgsT&&... args)
{
return getRemoteCallableWithBackChannel(std::move(name), std::forward<FunctionT>(cb))(
std::forward<ArgsT>(args)...);
}

/**
* @brief Get a callable remote function and register a temporary callable for a response.
*/
Expand Down
120 changes: 120 additions & 0 deletions nui/js/rpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
export type AnyFunction = (...args: any[]) => any;

class RpcClient {
constructor() {
}

public static UnresolvedError = class {
private message: string;
public readonly name = "UnresolvedRemoteCallableError";

constructor(name: string) {
this.message = `Remote callable with name '${name}' is undefined`;
}
};

private static resolve = (name: string) => {
const rpcObject = globalThis.nui_rpc;
if (rpcObject === undefined)
return undefined;

if (rpcObject.backend === undefined)
return undefined;

if (!rpcObject.backend.hasOwnProperty(name))
return undefined;

return rpcObject.backend[name];
}

public static getRemoteCallable(name: string) {
return (...args: any[]) : any => {
let resolved: AnyFunction | undefined = undefined;
const memoize = (): AnyFunction | undefined => {
if (resolved !== undefined)
return resolved;
resolved = RpcClient.resolve(name);
console.log(name, resolved);
return resolved;
};

return memoize() ? resolved!(...args) : new RpcClient.UnresolvedError(name);
}
}

public static getRemoteCallableWithBackChannel(name: string, cb: AnyFunction)
{
return (...args: any[]) : any => {
const tempId = globalThis.nui_rpc.tempId + 1;
globalThis.nui_rpc.tempId = tempId;

const tempIdString = `temp_${tempId}`;
globalThis.nui_rpc.backend[tempIdString] = (param: any) => {
cb(param);
delete globalThis.nui_rpc.backend[tempIdString];
};

const resolved = RpcClient.resolve(name);
if (resolved === undefined)
return new RpcClient.UnresolvedError(name);
return resolved(tempIdString, ...args);
}
}

public static call(name: string, ...args: any[]) {
if (args.length > 0 && typeof args[0] === 'function') {
const cb = args[0];
const restArgs = args.slice(1);
const callable = RpcClient.getRemoteCallableWithBackChannel(name, cb);
if (callable instanceof RpcClient.UnresolvedError)
return callable;
return callable(...restArgs);
} else {
const callable = RpcClient.getRemoteCallable(name);
if (callable instanceof RpcClient.UnresolvedError)
return callable;
return callable(...args);
}
}

// Only use for functions that respond via callback
public static callAsync(name: string, ...args: any[]): Promise<any> {
return new Promise((resolve, reject) => {
const callback = (result: any) => {
resolve(result);
};

const callable = RpcClient.getRemoteCallableWithBackChannel(name, callback);
if (callable instanceof RpcClient.UnresolvedError) {
reject(callable);
} else {
const result = callable(...args);
if (result instanceof RpcClient.UnresolvedError) {
reject(result);
}
}
});
}

public static register(name: string, func: AnyFunction) {
globalThis.nui_rpc.frontend[name] = func;
}

public static unregister(name: string) {
delete globalThis.nui_rpc.frontend[name];
}

public static registerMany(funcs: { [key: string]: AnyFunction }) {
for (const key in funcs) {
RpcClient.register(key, funcs[key]);
}
}

public static unregisterMany(names: string[]) {
for (const name of names) {
RpcClient.unregister(name);
}
}
}

export default RpcClient;
Loading