import { parseArgs } from "util"; let types = { u8: { c: "unsigned char", js: "Uint8", sql: "INTEGER", size: 1, }, i8: { c: "char", js: "Int8", sql: "INTEGER", size: 1, }, u16: { c: "unsigned short", js: "Uint16", sql: "INTEGER", size: 2, }, i16: { c: "short", js: "Int16", sql: "INTEGER", size: 2, }, u32: { c: "unisgned int", js: "Uint32", sql: "INTEGER", size: 4, }, i32: { c: "int", js: "Int32", sql: "INTEGER", size: 4, }, i64: { c: "long", js: "BigInt64", sql: "INTEGER" }, u64: { c: "unsigned long", js: "BigUint64", sql: "INTEGER" }, f32: { c: "float", js: "Float32", sql: "REAL", size: 4, }, f64: { c: "double", js: "Float64", sql: "REAL", size: 8, }, }; const { values } = parseArgs({ args: Bun.argv, options: { schema: { type: "string", short: "S", }, javascript_out: { type: "string", short: "j", }, c_out: { type: "string", short: "c", }, sqlite_out: { type: "string", short: "s" } }, strict: true, allowPositionals: true, }); function jsStructConstructor(size, containsString) { return ` get bytes() { return new Uint8Array(this._ptr); } constructor(init = {}, ptr = undefined) { this._size = ${size}; this._ptr = ptr.buffer || new ArrayBuffer(this._size); this._data = new DataView(this._ptr); ${ containsString ? ` this._encoder = new TextEncoder(); this._decoder = new TextDecoder();` : "" } for (const key of Object.keys(init)) { this[key] = init[key]; } }`; } const sFile = Bun.file(values.schema); const schema = await sFile.json(); let cData = ""; let sqlData = ""; for (const type of Object.keys(schema)) { let containsString = false; let offset = 0; let size = 0; let importStatements = ""; let jsData = ""; let foreignKeys = ""; const props = schema[type]; sqlData += `CREATE TABLE ${type} (${type.toLowerCase()}_id INTEGER PRIMARY KEY AUTOINCREMENT`; cData += `typedef struct ${type} {`; jsData += `class ${type} {`; for (const prop of Object.keys(props)) { const propType = props[prop].type; const kind = props[prop].kind; let typeSize = parseInt(types[propType]?.size); switch (kind) { case "string": containsString = true; typeSize = props[prop].size; const iSize = parseInt(offset) + parseInt(typeSize); jsData += ` get ${prop}() { return this._decoder.decode(new Uint8Array(this._ptr.slice(${parseInt( offset )}, ${iSize}))); } set ${prop}(v) { this._data.set(this._encoder.encode(v), ${parseInt(offset)}); }`; sqlData += `, ${prop} TEXT`; cData += ` char ${prop}[${iSize}];`; break; case "scalar": typeSize = types[propType].size; jsData += ` get ${prop}() { return this._data.get${types[propType].js}(${parseInt(offset)}, true); } set ${prop}(v) { return this._data.set${types[propType].js}(${parseInt( offset )}, v, true); }`; sqlData += `, ${prop} ${types[propType].sql}`; cData += ` ${types[propType].c} ${prop};`; break; case "struct": const jsSize = parseInt(offset) + parseInt(types[propType].size); jsData += ` get ${prop}() { return new ${propType}({}, new Uint8Array(this._ptr.slice(${offset}, ${jsSize}))); } set ${prop}(v) { this._data.set(v.bytes(), ${offset}); }`; const importS = `import ${propType} from "./${propType}"\n\n`; if (!importStatements.includes(importS)) { importStatements += importS; } const foreignKey = `${propType.toLowerCase()}_id`; const localKey = `${prop.toLowerCase()}_${foreignKey}`; sqlData += `, ${localKey} INTEGER`; foreignKeys += `\n, FOREIGN KEY(${localKey}) REFERENCES ${propType}(${foreignKey})` cData += `\n\t\t${types[propType].c} ${prop};`; break; case "array": throw new Error("Not Implemented!"); break; default: break; } size += parseInt(typeSize); offset += parseInt(typeSize); } /** * add the new type to the list so we can use it for structs later. */ types[type] = { c: type, js: type, size: parseInt(size), }; jsData += jsStructConstructor(size, containsString); jsData += `\n}\n\nexport default ${type}`; cData += `\n} ${type};\n\n`; sqlData += `${foreignKeys});\n\n`; await Bun.write( Bun.file(values.javascript_out + type + ".js"), importStatements + jsData ); } await Bun.write(Bun.file(values.c_out + "types.h"), cData); await Bun.write(Bun.file(values.sqlite_out + "types.sql"), sqlData);