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

  1. const fs = require('fs');
  2. const path = require('path');
  3. const LIBRDKAFKA_VERSION = require('../package.json').librdkafka;
  4. const LIBRDKAFKA_DIR = path.resolve(__dirname, '../deps/librdkafka/');
  5. function getHeader(file) {
  6. return `// ====== Generated from librdkafka ${LIBRDKAFKA_VERSION} file ${file} ======`;
  7. }
  8. function readLibRDKafkaFile(file) {
  9. return fs.readFileSync(path.resolve(LIBRDKAFKA_DIR, file)).toString();
  10. }
  11. function extractConfigItems(configStr) {
  12. const [_header, config] = configStr.split(/-{5,}\|.*/);
  13. const re = /(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*?)\|(.*)/g;
  14. const configItems = [];
  15. let m;
  16. do {
  17. m = re.exec(config);
  18. if (m) {
  19. const [
  20. _fullString,
  21. property,
  22. consumerOrProducer,
  23. range,
  24. defaultValue,
  25. importance,
  26. descriptionWithType,
  27. ] = m.map(el => (typeof el === 'string' ? el.trim() : el));
  28. const splitDescriptionRe = /(.*?)\s*?<br>.*?:\s.*?(.*?)\*/;
  29. const [_, description, rawType] = splitDescriptionRe.exec(descriptionWithType);
  30. configItems.push({
  31. property,
  32. consumerOrProducer,
  33. range,
  34. defaultValue,
  35. importance,
  36. description,
  37. rawType,
  38. });
  39. }
  40. } while (m);
  41. return configItems.map(processItem);
  42. }
  43. function processItem(configItem) {
  44. // These items are overwritten by node-rdkafka
  45. switch (configItem.property) {
  46. case 'dr_msg_cb':
  47. return { ...configItem, type: 'boolean' };
  48. case 'dr_cb':
  49. return { ...configItem, type: 'boolean | Function' };
  50. case 'rebalance_cb':
  51. return { ...configItem, type: 'boolean | Function' };
  52. case 'offset_commit_cb':
  53. return { ...configItem, type: 'boolean | Function' };
  54. }
  55. switch (configItem.rawType) {
  56. case 'integer':
  57. return { ...configItem, type: 'number' };
  58. case 'boolean':
  59. return { ...configItem, type: 'boolean' };
  60. case 'string':
  61. case 'CSV flags':
  62. return { ...configItem, type: 'string' };
  63. case 'enum value':
  64. return {
  65. ...configItem,
  66. type: configItem.range
  67. .split(',')
  68. .map(str => `'${str.trim()}'`)
  69. .join(' | '),
  70. };
  71. default:
  72. return { ...configItem, type: 'any' };
  73. }
  74. }
  75. function generateInterface(interfaceDef, configItems) {
  76. const fields = configItems
  77. .map(item =>
  78. [
  79. `/**`,
  80. ` * ${item.description}`,
  81. ...(item.defaultValue ? [` *`, ` * @default ${item.defaultValue}`] : []),
  82. ` */`,
  83. `"${item.property}"?: ${item.type};`,
  84. ]
  85. .map(row => ` ${row}`)
  86. .join('\n')
  87. )
  88. .join('\n\n');
  89. return `export interface ` + interfaceDef + ' {\n' + fields + '\n}';
  90. }
  91. function addSpecialGlobalProps(globalProps) {
  92. globalProps.push({
  93. "property": "event_cb",
  94. "consumerOrProducer": "*",
  95. "range": "",
  96. "defaultValue": "true",
  97. "importance": "low",
  98. "description": "Enables or disables `event.*` emitting.",
  99. "rawType": "boolean",
  100. "type": "boolean"
  101. });
  102. }
  103. function generateConfigDTS(file) {
  104. const configuration = readLibRDKafkaFile(file);
  105. const [globalStr, topicStr] = configuration.split('Topic configuration properties');
  106. const [globalProps, topicProps] = [extractConfigItems(globalStr), extractConfigItems(topicStr)];
  107. addSpecialGlobalProps(globalProps);
  108. const [globalSharedProps, producerGlobalProps, consumerGlobalProps] = [
  109. globalProps.filter(i => i.consumerOrProducer === '*'),
  110. globalProps.filter(i => i.consumerOrProducer === 'P'),
  111. globalProps.filter(i => i.consumerOrProducer === 'C'),
  112. ];
  113. const [topicSharedProps, producerTopicProps, consumerTopicProps] = [
  114. topicProps.filter(i => i.consumerOrProducer === '*'),
  115. topicProps.filter(i => i.consumerOrProducer === 'P'),
  116. topicProps.filter(i => i.consumerOrProducer === 'C'),
  117. ];
  118. let output = `${getHeader(file)}
  119. // Code that generated this is a derivative work of the code from Nam Nguyen
  120. // https://gist.github.com/ntgn81/066c2c8ec5b4238f85d1e9168a04e3fb
  121. `;
  122. output += [
  123. generateInterface('GlobalConfig', globalSharedProps),
  124. generateInterface('ProducerGlobalConfig extends GlobalConfig', producerGlobalProps),
  125. generateInterface('ConsumerGlobalConfig extends GlobalConfig', consumerGlobalProps),
  126. generateInterface('TopicConfig', topicSharedProps),
  127. generateInterface('ProducerTopicConfig extends TopicConfig', producerTopicProps),
  128. generateInterface('ConsumerTopicConfig extends TopicConfig', consumerTopicProps),
  129. ].join('\n\n');
  130. fs.writeFileSync(path.resolve(__dirname, '../config.d.ts'), output);
  131. }
  132. function updateErrorDefinitions(file) {
  133. const rdkafkacpp_h = readLibRDKafkaFile(file);
  134. const m = /enum ErrorCode {([^}]+)}/g.exec(rdkafkacpp_h);
  135. if (!m) {
  136. throw new Error(`Can't read rdkafkacpp.h file`)
  137. }
  138. const body = m[1]
  139. .replace(/(\t)|( +)/g, ' ')
  140. .replace(/\n\n/g, '\n')
  141. .replace(/\s+=\s+/g, ': ')
  142. .replace(/[\t ]*#define +(\w+) +(\w+)/g, (_, define, original) => {
  143. const value = new RegExp(`${original}\\s+=\\s+(\\d+)`).exec(m[1])[1];
  144. return ` ${define}: ${value},`;
  145. })
  146. // validate body
  147. const emptyCheck = body
  148. .replace(/(( \/\*)|( ?\*)).*/g, '')
  149. .replace(/ ERR_\w+: -?\d+,?\n/g, '')
  150. .trim()
  151. if (emptyCheck !== '') {
  152. throw new Error(`Fail to parse ${file}. It contains these extra details:\n${emptyCheck}`);
  153. }
  154. const error_js_file = path.resolve(__dirname, '../lib/error.js');
  155. const error_js = fs.readFileSync(error_js_file)
  156. .toString()
  157. .replace(/(\/\/.*\n)?LibrdKafkaError.codes = {[^}]+/g, `${getHeader(file)}\nLibrdKafkaError.codes = {\n${body}`)
  158. fs.writeFileSync(error_js_file, error_js);
  159. fs.writeFileSync(path.resolve(__dirname, '../errors.d.ts'), `${getHeader(file)}\nexport const CODES: { ERRORS: {${body.replace(/[ \.]*(\*\/\n \w+: )(-?\d+),?/g, ' (**$2**) $1number,')}}}`)
  160. }
  161. (async function updateTypeDefs() {
  162. generateConfigDTS('CONFIGURATION.md');
  163. updateErrorDefinitions('src-cpp/rdkafkacpp.h');
  164. })()