All files / transpiler/output/headers/generators generateStructHeader.ts

96.42% Statements 27/28
93.75% Branches 15/16
100% Functions 4/4
96.29% Lines 26/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101                      20x             124x 124x       124x       124x                               124x 124x   124x 121x     3x 3x   3x 2x   1x                                     57x     57x 2x     55x 55x   55x     55x 124x 124x 124x     55x   55x        
/**
 * Struct Header Generator
 *
 * Generates C typedef struct declarations from symbol information.
 * Used by HeaderGenerator to emit full struct definitions in headers.
 */
 
import IHeaderTypeInput from "./IHeaderTypeInput";
import typeUtils from "./mapType";
import CppNamespaceUtils from "../../../../utils/CppNamespaceUtils";
 
const { mapType } = typeUtils;
 
/**
 * Resolve the C type for a struct field, checking for callback types first.
 */
function resolveFieldCType(fieldType: string, input: IHeaderTypeInput): string {
  // ADR-029: Check if field type is a callback type and use typedef name
  const callbackInfo = input.callbackTypes?.get(fieldType);
  Iif (callbackInfo) {
    return callbackInfo.typedefName;
  }
  // Issue #502/#522: Convert C++ namespace types from _ to :: format
  const convertedType = CppNamespaceUtils.convertToCppNamespace(
    fieldType,
    input.symbolTable,
  );
  return mapType(convertedType);
}
 
/**
 * Generate the field line for a struct member, handling embedded dimensions.
 *
 * Issue #461: Handle string<N> types which map to char[N+1]
 * The embedded dimension must come after array dimensions in C syntax.
 * Example: string<64> arr[4] -> char arr[4][65], not char[65] arr[4]
 */
function generateFieldLine(
  fieldName: string,
  cType: string,
  dims: readonly number[] | undefined,
): string {
  const dimSuffix =
    dims && dims.length > 0 ? dims.map((d) => `[${d}]`).join("") : "";
  const embeddedMatch = /^(\w+)\[(\d+)\]$/.exec(cType);
 
  if (!embeddedMatch) {
    return `    ${cType} ${fieldName}${dimSuffix};`;
  }
 
  const baseType = embeddedMatch[1];
  const embeddedDim = embeddedMatch[2];
  // When dims are provided, they already include string capacity from StructCollector
  if (dims && dims.length > 0) {
    return `    ${baseType} ${fieldName}${dimSuffix};`;
  }
  return `    ${baseType} ${fieldName}[${embeddedDim}];`;
}
 
/**
 * Generate a C typedef struct declaration for the given struct name.
 *
 * Output format (Issue #296: uses named struct for forward declaration compatibility):
 * ```c
 * typedef struct StructName {
 *     uint32_t field1;
 *     uint8_t buffer[256];
 * } StructName;
 * ```
 *
 * @param name - The struct type name
 * @param input - Symbol information containing struct fields
 * @returns C typedef struct declaration, or forward declaration if data unavailable
 */
function generateStructHeader(name: string, input: IHeaderTypeInput): string {
  const fields = input.structFields.get(name);
 
  // Graceful fallback if struct data not available
  if (!fields || fields.size === 0) {
    return `typedef struct ${name} ${name};`;
  }
 
  const dimensions = input.structFieldDimensions.get(name);
  const lines: string[] = [];
  // Issue #296: Use named struct for forward declaration compatibility
  lines.push(`typedef struct ${name} {`);
 
  // Iterate fields in insertion order (Map preserves order)
  for (const [fieldName, fieldType] of fields) {
    const cType = resolveFieldCType(fieldType, input);
    const dims = dimensions?.get(fieldName);
    lines.push(generateFieldLine(fieldName, cType, dims));
  }
 
  lines.push(`} ${name};`);
 
  return lines.join("\n");
}
 
export default generateStructHeader;