import { FoxglovePrimitive, FoxgloveSchema } from "./types"; function primitiveToIdl(type: Exclude) { switch (type) { case "bytes": return "sequence"; case "string": return "string"; case "boolean": return "boolean"; case "float64": return "double"; case "uint32": return "uint32"; } } export const TIME_IDL = `\ module foxglove { struct Time { uint32 sec; uint32 nsec; }; }; `; export const DURATION_IDL = `\ module foxglove { struct Duration { int32 sec; uint32 nsec; }; }; `; export function generateOmgIdl(schema: FoxgloveSchema): string { const imports = new Set(); let definition: string; switch (schema.type) { case "enum": { const fields = schema.values.map(({ name, value, description }, index) => { const separator = index === schema.values.length - 1 ? "" : ","; if (value !== index) { throw new Error( `Enum value ${schema.name}.${name} at index ${index} has value ${value}; index and value must match for OMG IDL`, ); } if (description != undefined) { return `// ${description}\n // Value: ${value}\n ${name}${separator}`; } else { return `// Value: ${value}\n ${name}${separator}`; } }); definition = `// ${schema.description}\nenum ${schema.name} {\n ${fields.join( "\n\n ", )}\n};`; break; } case "message": { const fields = schema.fields.map((field) => { let fieldType: string; switch (field.type.type) { case "enum": fieldType = field.type.enum.name; imports.add(field.type.enum.name); break; case "nested": fieldType = field.type.schema.name; imports.add(field.type.schema.name); break; case "primitive": if (field.type.name === "time") { fieldType = "Time"; imports.add("Time"); } else if (field.type.name === "duration") { fieldType = "Duration"; imports.add("Duration"); } else { fieldType = primitiveToIdl(field.type.name); } break; } let arraySize = ""; if (typeof field.array === "number") { arraySize = `[${field.array}]`; } else if (field.array != undefined) { fieldType = `sequence<${fieldType}>`; } const descriptionLines = field.description.trim().split("\n"); const comment = descriptionLines.map((line) => `// ${line}`).join("\n "); let defaultAnnotation = ""; if (typeof field.defaultValue === "string") { defaultAnnotation = `@default(${JSON.stringify(field.defaultValue)})\n `; } else if (typeof field.defaultValue === "number") { // For floating-point fields with integer default values, ensure we output a number with // at least one decimal place so it is interpreted as an IDL floating-point literal if ( field.type.type === "primitive" && field.type.name === "float64" && Number.isInteger(field.defaultValue) ) { defaultAnnotation = `@default(${field.defaultValue.toFixed(1)})\n `; } else { defaultAnnotation = `@default(${field.defaultValue})\n `; } } else if (typeof field.defaultValue === "boolean") { defaultAnnotation = `@default(${field.defaultValue ? "TRUE" : "FALSE"})\n `; } return `${comment}\n ${defaultAnnotation}${fieldType} ${field.name}${arraySize};`; }); definition = `// ${schema.description}\nstruct ${schema.name} {\n ${fields.join( "\n\n ", )}\n};`; break; } } const outputSections = [ `// Generated by https://github.com/foxglove/schemas`, Array.from(imports) .sort() .map((name) => `#include "foxglove/${name}.idl"`) .join("\n"), "module foxglove {", definition, "};", ].filter(Boolean); return outputSections.join("\n\n") + "\n"; }