You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

193 lines
5.9 KiB

const fs = require('fs');
const path = require('path');
const LIBRDKAFKA_VERSION = require('../package.json').librdkafka;
const LIBRDKAFKA_DIR = path.resolve(__dirname, '../deps/librdkafka/');
function getHeader(file) {
return `// ====== Generated from librdkafka ${LIBRDKAFKA_VERSION} file ${file} ======`;
}
function readLibRDKafkaFile(file) {
return fs.readFileSync(path.resolve(LIBRDKAFKA_DIR, file)).toString();
}
function extractConfigItems(configStr) {
const [_header, config] = configStr.split(/-{5,}\|.*/);
const re = /(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*)/g;
const configItems = [];
let m;
do {
m = re.exec(config);
if (m) {
const [
_fullString,
property,
consumerOrProducer,
range,
defaultValue,
importance,
descriptionWithType,
] = m.map(el => (typeof el === 'string' ? el.trim() : el));
const splitDescriptionRe = /(.*?)\s*?<br>.*?:\s.*?(.*?)\*/;
const [_, description, rawType] = splitDescriptionRe.exec(descriptionWithType);
configItems.push({
property,
consumerOrProducer,
range,
defaultValue,
importance,
description,
rawType,
});
}
} while (m);
return configItems.map(processItem);
}
function processItem(configItem) {
// These items are overwritten by node-rdkafka
switch (configItem.property) {
case 'dr_msg_cb':
return { ...configItem, type: 'boolean' };
case 'dr_cb':
return { ...configItem, type: 'boolean | Function' };
case 'rebalance_cb':
return { ...configItem, type: 'boolean | Function' };
case 'offset_commit_cb':
return { ...configItem, type: 'boolean | Function' };
}
switch (configItem.rawType) {
case 'integer':
return { ...configItem, type: 'number' };
case 'boolean':
return { ...configItem, type: 'boolean' };
case 'string':
case 'CSV flags':
return { ...configItem, type: 'string' };
case 'enum value':
return {
...configItem,
type: configItem.range
.split(',')
.map(str => `'${str.trim()}'`)
.join(' | '),
};
default:
return { ...configItem, type: 'any' };
}
}
function generateInterface(interfaceDef, configItems) {
const fields = configItems
.map(item =>
[
`/**`,
` * ${item.description}`,
...(item.defaultValue ? [` *`, ` * @default ${item.defaultValue}`] : []),
` */`,
`"${item.property}"?: ${item.type};`,
]
.map(row => ` ${row}`)
.join('\n')
)
.join('\n\n');
return `export interface ` + interfaceDef + ' {\n' + fields + '\n}';
}
function addSpecialGlobalProps(globalProps) {
globalProps.push({
"property": "event_cb",
"consumerOrProducer": "*",
"range": "",
"defaultValue": "true",
"importance": "low",
"description": "Enables or disables `event.*` emitting.",
"rawType": "boolean",
"type": "boolean"
});
}
function generateConfigDTS(file) {
const configuration = readLibRDKafkaFile(file);
const [globalStr, topicStr] = configuration.split('Topic configuration properties');
const [globalProps, topicProps] = [extractConfigItems(globalStr), extractConfigItems(topicStr)];
addSpecialGlobalProps(globalProps);
const [globalSharedProps, producerGlobalProps, consumerGlobalProps] = [
globalProps.filter(i => i.consumerOrProducer === '*'),
globalProps.filter(i => i.consumerOrProducer === 'P'),
globalProps.filter(i => i.consumerOrProducer === 'C'),
];
const [topicSharedProps, producerTopicProps, consumerTopicProps] = [
topicProps.filter(i => i.consumerOrProducer === '*'),
topicProps.filter(i => i.consumerOrProducer === 'P'),
topicProps.filter(i => i.consumerOrProducer === 'C'),
];
let output = `${getHeader(file)}
// Code that generated this is a derivative work of the code from Nam Nguyen
// https://gist.github.com/ntgn81/066c2c8ec5b4238f85d1e9168a04e3fb
`;
output += [
generateInterface('GlobalConfig', globalSharedProps),
generateInterface('ProducerGlobalConfig extends GlobalConfig', producerGlobalProps),
generateInterface('ConsumerGlobalConfig extends GlobalConfig', consumerGlobalProps),
generateInterface('TopicConfig', topicSharedProps),
generateInterface('ProducerTopicConfig extends TopicConfig', producerTopicProps),
generateInterface('ConsumerTopicConfig extends TopicConfig', consumerTopicProps),
].join('\n\n');
fs.writeFileSync(path.resolve(__dirname, '../config.d.ts'), output);
}
function updateErrorDefinitions(file) {
const rdkafkacpp_h = readLibRDKafkaFile(file);
const m = /enum ErrorCode {([^}]+)}/g.exec(rdkafkacpp_h);
if (!m) {
throw new Error(`Can't read rdkafkacpp.h file`)
}
const body = m[1]
.replace(/(\t)|( +)/g, ' ')
.replace(/\n\n/g, '\n')
.replace(/\s+=\s+/g, ': ')
.replace(/[\t ]*#define +(\w+) +(\w+)/g, (_, define, original) => {
const value = new RegExp(`${original}\\s+=\\s+(\\d+)`).exec(m[1])[1];
return ` ${define}: ${value},`;
})
// validate body
const emptyCheck = body
.replace(/(( \/\*)|( ?\*)).*/g, '')
.replace(/ ERR_\w+: -?\d+,?\n/g, '')
.trim()
if (emptyCheck !== '') {
throw new Error(`Fail to parse ${file}. It contains these extra details:\n${emptyCheck}`);
}
const error_js_file = path.resolve(__dirname, '../lib/error.js');
const error_js = fs.readFileSync(error_js_file)
.toString()
.replace(/(\/\/.*\n)?LibrdKafkaError.codes = {[^}]+/g, `${getHeader(file)}\nLibrdKafkaError.codes = {\n${body}`)
fs.writeFileSync(error_js_file, error_js);
fs.writeFileSync(path.resolve(__dirname, '../errors.d.ts'), `${getHeader(file)}\nexport const CODES: { ERRORS: {${body.replace(/[ \.]*(\*\/\n \w+: )(-?\d+),?/g, ' (**$2**) $1number,')}}}`)
}
(async function updateTypeDefs() {
generateConfigDTS('CONFIGURATION.md');
updateErrorDefinitions('src-cpp/rdkafkacpp.h');
})()