Files
STM32F4-Base/IEC60870-Slave/.build/command-docs/check-api.cjs
T
STF cd42b4d61d
CI / L0 静态门禁 (pre-commit) (push) Failing after 2m51s
CI / L0.5 主机侧单测 (MSVC) (push) Successful in 18s
添加遥控业务代码
2026-10-30 20:03:19 -07:00

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;