2020-09-08 14:59:06 +00:00
|
|
|
/**
|
|
|
|
* @author Martin Karkowski
|
|
|
|
* @email m.karkowski@zema.de
|
|
|
|
* @create date 2019-01-09 18:10:33
|
2020-09-10 16:21:19 +00:00
|
|
|
* @modify date 2020-09-09 08:35:46
|
2020-09-08 14:59:06 +00:00
|
|
|
* @desc [description]
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This File provides handy tools to extract the services and packages of a proto file.
|
|
|
|
* In Addition, it matches the input and outputs of a function to the function it self.
|
|
|
|
*
|
|
|
|
* To provide this tool, REGULAR-EXPRESSIONS are used. A good starting point and tool
|
|
|
|
* to test your regular expression is provided at https://regexr.com/
|
|
|
|
*
|
|
|
|
* The used REGULAR-EXPRESSIONS (in short regex) are stored in the constants below.
|
|
|
|
*
|
|
|
|
* A File is analyse by extracting all defined packages in the file and split up the
|
|
|
|
* content based on that packages.
|
|
|
|
*
|
|
|
|
* ### TEXT ####
|
|
|
|
* package test;
|
|
|
|
* ### More TEXT ### ==> contains to Package 1
|
|
|
|
* package test2;
|
|
|
|
* ### And More TEXT ### ==> Contains to Package 2
|
|
|
|
*
|
|
|
|
* After extracting the content of a Package, all service Providers are extracted with
|
|
|
|
* the same concept:
|
|
|
|
* ### TEXT ####
|
|
|
|
* package test;
|
|
|
|
* ### TEXT ###
|
|
|
|
* service TestService { ==> Split with this line. Analayse the following stuff until the next service
|
|
|
|
* // Comments etc
|
|
|
|
* rpc testCall (stream InputTypeName) returns (otherPackage.Message) {}
|
|
|
|
* }
|
|
|
|
* ...
|
|
|
|
*
|
|
|
|
* service TestService2 { ==> Split here again
|
|
|
|
* // Comments etc
|
|
|
|
* rpc testCall (stream InputTypeName) returns (otherPackage.Message) {}
|
|
|
|
* }
|
|
|
|
* ...
|
|
|
|
*
|
|
|
|
* Thereby all available RPCs of a Service are extracted. They are finially analyzed
|
|
|
|
* and the Information is assembled in a specified Data-Structure.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
import { readFileSync } from "fs";
|
2020-09-10 16:21:19 +00:00
|
|
|
import { listFiles } from "../../../lib/helpers/fileMethods";
|
|
|
|
|
2020-09-08 14:59:06 +00:00
|
|
|
|
|
|
|
const REG_SERVICE = /service\s*\w+\s*\{\s*\n/g;
|
|
|
|
const REG_GETSERVICENAME = /\w+/g;
|
|
|
|
const REG_RPC = /rpc \w+\s*\((stream)?\s*\w+\.?\w*\)\s*returns\s*\((stream)?\s*\w+\.?\w*\)\s\{\}*/g;
|
|
|
|
const REG_RPC_DETAILS = /[\w+\.?]*/g
|
|
|
|
const REG_GET_TYPES = /\w*[\.\w]*(?!\()(?=\))/g;
|
|
|
|
const REG_PACKAGE = /package \w[\.\w]*/;
|
|
|
|
const REG_GETPACKAGENAME = /[^ ]*/g;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fucntion to Analyse a File and extract all Packages, Service-Providers and their RPCs with
|
|
|
|
* the defined inputs and outputs
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {string} _fileContent the content of the file
|
|
|
|
* @param {*} [_ret={}] A Object which will be extended
|
|
|
|
* @returns
|
|
|
|
*/
|
|
|
|
export function analyseFile(_fileContent: string, _ret: any = {}) {
|
|
|
|
|
|
|
|
let _files = new Array<{ package: string, content: string }>();
|
|
|
|
|
|
|
|
if (REG_PACKAGE.test(_fileContent)) {
|
|
|
|
|
|
|
|
const _packages = (_fileContent.match(REG_PACKAGE) as Array<string>).filter(value => value ? value : undefined);
|
|
|
|
|
|
|
|
for (const _package of _packages) {
|
|
|
|
/** Extract the Content after the Service-Definition */
|
|
|
|
let _splitted = _fileContent.split(_package)[1];
|
|
|
|
|
|
|
|
/** Test if a Service follows => If so, remove the second servicer */
|
|
|
|
if (REG_PACKAGE.test(_splitted)) {
|
|
|
|
_splitted = (_splitted.match(REG_PACKAGE) as Array<string>).filter(value => value ? value : undefined)[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
_files.push({
|
|
|
|
package: (_package.match(REG_GETPACKAGENAME) as Array<string>).filter(value => value ? value : undefined)[1],
|
|
|
|
content: _splitted
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
_files.push({
|
|
|
|
package: '',
|
|
|
|
content: _fileContent
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return getServiceProviders(_files, _ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to extract the Service Provider
|
|
|
|
*
|
|
|
|
* @param {Array<{ package: string, content: string }>} packages List with Packages
|
|
|
|
* @param _ret Element which will contain the information
|
|
|
|
* @returns
|
|
|
|
*/
|
|
|
|
function getServiceProviders(packages: Array<{ package: string, content: string }>, _ret: {
|
|
|
|
/** Package */
|
|
|
|
[index: string]: {
|
|
|
|
/** Servicer */
|
|
|
|
[index: string]: {
|
|
|
|
methods: {
|
|
|
|
/** Method */
|
|
|
|
[index: string]: {
|
|
|
|
input: {
|
|
|
|
path: string,
|
|
|
|
stream: boolean,
|
|
|
|
},
|
|
|
|
output: {
|
|
|
|
path: string,
|
|
|
|
stream: boolean,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}) {
|
|
|
|
|
|
|
|
for (const _package of packages) {
|
|
|
|
if (REG_SERVICE.test(_package.content)) {
|
|
|
|
const _services = (_package.content.match(REG_SERVICE) as Array<string>).filter(value => value ? value : undefined);
|
|
|
|
|
|
|
|
if (_ret[_package.package] === undefined) {
|
|
|
|
_ret[_package.package] = {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const _srv of _services) {
|
|
|
|
/** Extract the Content after the Service-Definition */
|
|
|
|
let _splitted = _package.content.split(_srv)[1];
|
|
|
|
|
|
|
|
/** Test if a Service follows => If so, remove the second servicer */
|
|
|
|
if (REG_SERVICE.test(_splitted)) {
|
|
|
|
_splitted = _splitted.split((_splitted.match(REG_SERVICE) as Array<any>).filter(value => value ? value : undefined)[0])[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
const _srvName = (_srv.match(REG_GETSERVICENAME) as Array<string>)[1];
|
|
|
|
if (_ret[_package.package][_srvName] === undefined) {
|
|
|
|
_ret[_package.package][_srvName] = {
|
|
|
|
methods: {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Extract all RPCs */
|
|
|
|
if (REG_RPC.test(_splitted)) {
|
|
|
|
|
|
|
|
const rpcs = (_splitted.match(REG_RPC) as Array<string>)
|
|
|
|
/** Filter the results to remove undefined Elements */
|
|
|
|
.filter(value => value ? value : undefined);
|
|
|
|
|
|
|
|
for (const _rpc of rpcs) {
|
|
|
|
_ret[_package.package][_srvName].methods = Object.assign(
|
|
|
|
_ret[_package.package][_srvName].methods,
|
|
|
|
analyseRpc(_rpc, _package.package)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return _ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function for analysing a string like: 'rpc get_position_stream (std_package.Frequency) returns (stream std_package.Point) {}'
|
|
|
|
* To extract the Definition
|
|
|
|
*
|
|
|
|
* @param {string} _definition
|
|
|
|
* @param {string} _package
|
|
|
|
* @returns
|
|
|
|
*/
|
|
|
|
function analyseRpc(_definition: string, _package: string) {
|
|
|
|
/** Extract the Name */
|
|
|
|
const name = (_definition.match(REG_RPC_DETAILS) as Array<string>).filter(value => value ? value : undefined)[1];
|
|
|
|
|
|
|
|
/** Get the Input and Output Type */
|
|
|
|
const input = (_definition.match(REG_GET_TYPES) as Array<string>).filter(value => value ? value : undefined)[0];
|
|
|
|
const output = (_definition.match(REG_GET_TYPES) as Array<string>).filter(value => value ? value : undefined)[1];
|
|
|
|
|
|
|
|
/** Split with the Input type, to figure out whether something is streaming or not */
|
|
|
|
const _splitted = _definition.split(input);
|
|
|
|
|
|
|
|
/** Check for the corresponding keyword */
|
|
|
|
const streamInput = _splitted[0].match(/\(\s*stream/) !== null;
|
|
|
|
const streamOutput = _splitted[1].match(/\(\s*stream/) !== null;
|
|
|
|
|
|
|
|
/** Define the Return Type */
|
|
|
|
const _ret: {
|
|
|
|
[index: string]: {
|
|
|
|
/** Input of the Method */
|
|
|
|
input: {
|
|
|
|
path: string,
|
|
|
|
stream: boolean,
|
|
|
|
},
|
|
|
|
|
|
|
|
/** Output of the Method */
|
|
|
|
output: {
|
|
|
|
path: string,
|
|
|
|
stream: boolean,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
} = {};
|
|
|
|
_ret[name] = {
|
|
|
|
input: {
|
|
|
|
path: 'definitions.' + (input.includes('.') ? input : _package + '.' + input),
|
|
|
|
stream: streamInput,
|
|
|
|
},
|
|
|
|
output: {
|
|
|
|
path: 'definitions.' + (output.includes('.') ? output : _package + '.' + output),
|
|
|
|
stream: streamOutput,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
return _ret;
|
|
|
|
}
|
|
|
|
|
2020-09-10 16:21:19 +00:00
|
|
|
export async function scanAllFiles(_dir: string = './Proto-Repository/protos', _elements: {
|
2020-09-08 14:59:06 +00:00
|
|
|
/** Package */
|
|
|
|
[index: string]: {
|
|
|
|
/** Servicer */
|
|
|
|
[index: string]: {
|
|
|
|
methods: {
|
|
|
|
/** Method */
|
|
|
|
[index: string]: {
|
|
|
|
input: {
|
|
|
|
path: string,
|
|
|
|
stream: boolean,
|
|
|
|
},
|
|
|
|
output: {
|
|
|
|
path: string,
|
|
|
|
stream: boolean,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} = {}) {
|
|
|
|
|
|
|
|
/** Scan fro Proto-Files in the given Dir */
|
2020-09-10 16:21:19 +00:00
|
|
|
const files = await listFiles(_dir, '.proto');
|
2020-09-08 14:59:06 +00:00
|
|
|
|
|
|
|
/** Load all Files and analyse them */
|
|
|
|
for (const file of files) {
|
|
|
|
|
|
|
|
/** Read-In the File */
|
|
|
|
const _file = readFileSync(file, { encoding: 'utf-8' });
|
|
|
|
|
|
|
|
_elements = (analyseFile(_file, _elements));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return the result */
|
|
|
|
return _elements;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (require.main == module) {
|
|
|
|
let file = 'C:\\Users\\m.karkowski\\Documents\\Repos\\ZeMA-Kernel\\Proto-Repository\\protos\\robot.proto';
|
|
|
|
|
|
|
|
let content = readFileSync(file, { encoding: 'utf-8' })
|
|
|
|
|
|
|
|
console.log(analyseFile(content))
|
|
|
|
}
|