milsko-node/scripts/bindgen.js
2026-04-28 13:11:03 -07:00

163 lines
4.4 KiB
JavaScript

import { XMLParser } from "fast-xml-parser";
import { createWriteStream } from "node:fs";
import { createRequire } from "node:module";
const { upstream } = createRequire(import.meta.url)("../package.json");
const source =
"https://forgejo.nishi.boats/api/v1/repos/pyrite-dev/milsko/tags";
// Out files
const structsOut = createWriteStream("lib/structs.ts");
const enumsOut = createWriteStream("lib/enums.ts");
const constantsOut = createWriteStream("lib/constants.ts");
function toCamelCase(input) {
const split = input.split("_");
for (let i = 0; i < split.length; i++) {
const firstChar =
i <= 0
? split[i].charAt(0).toLowerCase()
: split[i].charAt(0).toUpperCase();
split[i] = firstChar + split[i].slice(1);
}
return split.join("");
}
function typeToTypescript(type) {
switch (type) {
case "integer":
return "number";
case "short":
return "number";
case "string":
return "string";
default:
"any";
}
}
function scanStructs(root) {
const list = root["structs"]["struct"];
for (const struct of list) {
console.log(struct);
const structName = struct["@_name"];
structsOut.write("export interface " + structName + " {\n");
for (const [type, children] of Object.entries(struct)) {
// attributes start with @_
if (!type.startsWith("@_") && Array.isArray(children)) {
for (const child of children) {
const attributeName = toCamelCase(child["@_name"]);
structsOut.write(
attributeName + ": " + typeToTypescript(type) + ";\n"
);
}
}
}
structsOut.write("}\n\n");
}
structsOut.end();
}
function scanEnumerations(root) {
const list = root["enumerations"]["enumeration"];
for (const enumeration of list) {
const enumName = enumeration["@_name"];
enumsOut.write("export enum " + enumName + " {\n");
for (const [type, children] of Object.entries(enumeration)) {
// attributes start with @_
if (!type.startsWith("@_")) {
for (const child of children) {
const attributeName = child["@_name"];
const attributeValue = child["#text"];
enumsOut.write(attributeName);
if (attributeValue) enumsOut.write(" = " + attributeValue);
enumsOut.write(",\n");
}
}
}
enumsOut.write("}\n\n");
}
enumsOut.end();
}
function scanConstants(root) {
const list = root["constants"];
for (const [type, children] of Object.entries(list)) {
for (const child of children) {
const constName = child["@_name"];
const constValue = child["#text"];
constantsOut.write(
"export const " +
constName +
": " +
typeToTypescript(type) +
" = " +
constValue +
";\n"
);
}
}
constantsOut.end();
}
async function bindgen() {
const parser = new XMLParser({
attributeNamePrefix: "@_",
ignoreAttributes: false,
parseAttributeValue: true
});
const response = await fetch(source);
if (!response.ok) {
throw new Error(
"Failed to fetch the Milsko repository tags, try running npm run install or npm run update again (" +
response.status +
")"
);
}
const tags = await response.json();
const match = tags.find((k) => k.name == upstream);
if (!match) {
throw new Error(
"Could not find a tag that matches the upstream version " + upstream
);
}
const commitHash = match["id"]
const specURL = "https://forgejo.nishi.boats/pyrite-dev/milsko/raw/commit/" +
commitHash + "/milsko.xml";
const spac = await fetch(specURL);
if (!spac.ok) {
throw new Error(
"Failed to fetch the XML API specification with status code " +
spac.status
);
}
const raw = await spac.text();
const root = parser.parse(raw)["milsko"];
scanStructs(root);
scanEnumerations(root);
scanConstants(root);
}
bindgen();