generateOneOfClass function

String generateOneOfClass(
  1. String className,
  2. Map<String, dynamic> def,
  3. Map<String, dynamic> allDefs,
  4. List<String> oneofFields,
)

Generates a sealed class hierarchy for types with oneOf fields.

The base sealed class contains all non-oneOf fields plus an abstract toJson. Each oneOf field becomes a final class variant that extends the base and carries that one field as required. A factory fromJson on the base dispatches to the correct variant by checking which key is present. Generates a single class with named constructors for oneOf types.

Each oneOf field gets a named constructor (e.g. .oidcToken(...)) that initialises that field as required and null-initialises all other oneOf fields. Base fields appear in every constructor. This mirrors the JS codegen's ONEOF_FIELDS treatment while staying idiomatic Dart.

Implementation

String generateOneOfClass(
  String className,
  Map<String, dynamic> def,
  Map<String, dynamic> allDefs,
  List<String> oneofFields,
) {
  final props = (def['properties'] as Map?)?.cast<String, dynamic>() ?? {};
  final requiredList = (def['required'] is List)
      ? (def['required'] as List).cast<String>()
      : <String>[];
  final oneofSet = Set<String>.from(oneofFields);

  final baseFields = <FieldSpec>[];
  final variantSpecs = <String, FieldSpec>{};

  for (final entry in props.entries) {
    final jsonKey = entry.key;
    final schema = (entry.value as Map).cast<String, dynamic>();
    String dartType = 'dynamic';
    if (schema[r'$ref'] is String) {
      dartType = refToName(schema[r'$ref'] as String);
    } else if (schema['type'] is String) {
      dartType = typeToDart(schema['type'] as String, schema);
    }
    final isRequired = requiredList.contains(jsonKey);
    final fieldName = sanitizeFieldName(jsonKey);
    if (oneofSet.contains(jsonKey)) {
      variantSpecs[jsonKey] = FieldSpec(
        fieldName: fieldName,
        jsonKey: jsonKey,
        type: dartType, // non-nullable in constructors, nullable as field
        nullable: true,
        description: schema['description'] as String?,
      );
    } else {
      baseFields.add(FieldSpec(
        fieldName: fieldName,
        jsonKey: jsonKey,
        type: isRequired ? dartType : '$dartType?',
        nullable: !isRequired,
        description: schema['description'] as String?,
      ));
    }
  }

  final b = StringBuffer();
  b.writeln('class $className {');

  // Base fields
  for (final f in baseFields) {
    if ((f.description ?? '').isNotEmpty) {
      b.writeln('  /// ${sanitizeDocComment(f.description)}');
    }
    b.writeln('  final ${f.type} ${f.fieldName};');
  }
  // oneOf fields — nullable on the class itself
  for (final jsonKey in oneofFields) {
    final spec = variantSpecs[jsonKey]!;
    if ((spec.description ?? '').isNotEmpty) {
      b.writeln('  /// ${sanitizeDocComment(spec.description)}');
    }
    b.writeln('  final ${spec.type}? ${spec.fieldName};');
  }
  b.writeln();

  // Named constructors — one per oneOf variant
  for (final jsonKey in oneofFields) {
    final spec = variantSpecs[jsonKey]!;
    b.writeln('  const $className.${spec.fieldName}({');
    for (final f in baseFields) {
      final req = f.nullable ? '' : 'required ';
      b.writeln('    $req this.${f.fieldName},');
    }
    b.writeln('    required ${spec.type} this.${spec.fieldName},');
    b.write('  })');
    final others = oneofFields.where((k) => k != jsonKey).toList();
    if (others.isNotEmpty) {
      b.write(' : ');
      b.write(
          others.map((k) => '${variantSpecs[k]!.fieldName} = null').join(', '));
    }
    b.writeln(';');
    b.writeln();
  }

  // fromJson factory
  b.writeln('  factory $className.fromJson(Map<String, dynamic> json) {');
  for (final jsonKey in oneofFields) {
    final spec = variantSpecs[jsonKey]!;
    b.writeln("    if (json['$jsonKey'] != null) {");
    b.writeln('      return $className.${spec.fieldName}(');
    for (final f in baseFields) {
      final read = "json['${f.jsonKey}']";
      final parse = f.type.endsWith('?')
          ? _jsonReadExpr(
              f.type.substring(0, f.type.length - 1), read, allDefs, false)
          : _jsonReadExpr(f.type, read, allDefs, true);
      b.writeln('        ${f.fieldName}: $parse,');
    }
    final variantParse =
        _jsonReadExpr(spec.type, "json['$jsonKey']", allDefs, true);
    b.writeln('        ${spec.fieldName}: $variantParse,');
    b.writeln('      );');
    b.writeln('    }');
  }
  b.writeln(
    "    throw ArgumentError('$className: none of the oneOf fields "
    "(${oneofFields.join(', ')}) are present in json');",
  );
  b.writeln('  }\n');

  // toJson
  b.writeln('  Map<String, dynamic> toJson() {');
  b.writeln('    final _json = <String, dynamic>{};');
  for (final f in baseFields) {
    final writeExpr = _jsonWriteExpr(f.type, f.fieldName, allDefs);
    if (f.nullable) {
      b.writeln("    if (${f.fieldName} != null) {");
      b.writeln("      _json['${f.jsonKey}'] = $writeExpr;");
      b.writeln('    }');
    } else {
      b.writeln("    _json['${f.jsonKey}'] = $writeExpr;");
    }
  }
  for (final jsonKey in oneofFields) {
    final spec = variantSpecs[jsonKey]!;
    final writeExpr = _jsonWriteExpr('${spec.type}?', spec.fieldName, allDefs);
    b.writeln("    if (${spec.fieldName} != null) {");
    b.writeln("      _json['$jsonKey'] = $writeExpr;");
    b.writeln('    }');
  }
  b.writeln('    return _json;');
  b.writeln('  }');
  b.writeln('}\n');

  return b.toString();
}