59 lines
3.0 KiB
JavaScript
59 lines
3.0 KiB
JavaScript
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const root = path.resolve(__dirname, '../..');
|
|
const document = fs.readFileSync(path.join(root, 'docs/104/IEC104_API接口汇总.md'), 'utf8');
|
|
const headers = [];
|
|
function walk(folder) {
|
|
for (const entry of fs.readdirSync(folder, { withFileTypes: true })) {
|
|
const location = path.join(folder, entry.name);
|
|
if (entry.isDirectory()) walk(location);
|
|
else if (entry.name.endsWith('.h') && !entry.name.endsWith('_Internal.h')) headers.push(location);
|
|
}
|
|
}
|
|
walk(path.join(root, '104'));
|
|
walk(path.join(root, 'common'));
|
|
const stripComments = source => source.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
.replace(/\/\/[^\n]*/g, '')
|
|
.replace(/__attribute__\s*\(\(format\(printf,\s*2,\s*3\)\)\)/g, '');
|
|
const signaturePattern = /^[ \t]*((?:extern\s+)?(?:const\s+)?[A-Za-z_]\w*(?:[ \t]+[A-Za-z_]\w*)*[ \t*]+(IEC(?:104|60870)_[A-Za-z0-9_]+)\s*\([^;{}]*\)\s*;)/gm;
|
|
const tokens = source => (source.replace(/^extern\s+/, '').match(/[A-Za-z_][A-Za-z0-9_]*|[0-9]+|[^\s]/g) || []).join(' ');
|
|
const declarationOnly = new Set(['IEC60870_Clock_RTC_Read', 'IEC60870_Clock_RTC_Set']);
|
|
const documented = new Map();
|
|
for (const block of document.matchAll(/```c\n([\s\S]*?)```/g)) {
|
|
for (const match of stripComments(block[1]).matchAll(signaturePattern)) {
|
|
if (/^typedef\b/.test(match[1])) continue;
|
|
if (!documented.has(match[2])) documented.set(match[2], new Set());
|
|
documented.get(match[2]).add(tokens(match[1]));
|
|
}
|
|
}
|
|
const missing = [], mismatched = [];
|
|
let checked = 0;
|
|
for (const header of headers) {
|
|
for (const match of stripComments(fs.readFileSync(header, 'utf8')).matchAll(signaturePattern)) {
|
|
if (/^typedef\b/.test(match[1])) continue;
|
|
const name = match[2];
|
|
if (declarationOnly.has(name)) continue;
|
|
++checked;
|
|
if (!documented.has(name)) missing.push({ header: path.relative(root, header), name });
|
|
else if (!documented.get(name).has(tokens(match[1]))) {
|
|
mismatched.push({ header: path.relative(root, header), name, declaration: match[1], documented: [...documented.get(name)] });
|
|
}
|
|
}
|
|
}
|
|
const commandHeader = stripComments(fs.readFileSync(path.join(root, '104/business/IEC104_Command.h'), 'utf8'));
|
|
const commandReference = document.slice(document.indexOf('## 25. IEC104_Command.c'));
|
|
const missingFields = [];
|
|
let checkedCommandFields = 0;
|
|
for (const structure of commandHeader.matchAll(/typedef\s+struct\s*\{([^{}]*)\}\s*(IEC104_Command\w+_t)\s*;/g)) {
|
|
for (const declaration of structure[1].split(';').map(x => x.trim()).filter(Boolean)) {
|
|
const names = declaration.split(',').map(x => /([A-Za-z_]\w*)\s*$/.exec(x)[1]);
|
|
for (const name of names) {
|
|
++checkedCommandFields;
|
|
if (!new RegExp(`\\b${name}\\b`).test(commandReference)) missingFields.push(`${structure[2]}.${name}`);
|
|
}
|
|
}
|
|
}
|
|
console.log(JSON.stringify({ checked, documented: documented.size, declarationOnly: [...declarationOnly],
|
|
missing, mismatched, checkedCommandFields, missingFields }, null, 2));
|
|
process.exitCode = missing.length || mismatched.length || missingFields.length ? 1 : 0;
|