dvStudioModelDartSource function

String dvStudioModelDartSource(
  1. DVStudioModelSpec spec
)

The @DVModel spec compiles back from, as a file in lib/models.

Every rule the definition carries is written as the annotation that declares it, so the generator reads the same spec back: the key first, choices as an enum, validation as @DVModel.validate, uniqueness as @DVModel.uniqueField, indexes and access on the model.

Implementation

String dvStudioModelDartSource(DVStudioModelSpec spec) {
  String quote(String value) =>
      "'${value.replaceAll(r'\', r'\\').replaceAll("'", r"\'").replaceAll(r'$', r'\$')}'";
  final StringBuffer out = StringBuffer()
    ..writeln(dvStudioModelSourceMarker)
    ..writeln('// Studio rewrites this file while that line is first. Delete the')
    ..writeln('// line to take the model over by hand; Studio then leaves it be.')
    ..writeln("import 'package:dartvel_core/dartvel.dart';")
    ..writeln();
  final Set<String> enums = <String>{};
  for (final DVStudioFieldSpec field in spec.fields) {
    final List<String>? options = field.options;
    if (options == null) continue;
    final String type = dvStudioBaseType(field.type);
    if (!enums.add(type)) continue;
    out
      ..writeln('enum $type { ${options.join(', ')} }')
      ..writeln();
  }
  final List<String> arguments = <String>[
    // A designed model has no page of its own until one is designed for it.
    'generatePages: false',
    if (!spec.versioned) 'version: false',
    if (spec.softDelete) 'softDelete: true',
    if (spec.access case final DVModelAccess access)
      'access: DVModelAccess('
          'view: DVAccess.${access.view.name}, '
          'create: DVAccess.${access.create.name}, '
          'update: DVAccess.${access.update.name}, '
          'delete: DVAccess.${access.delete.name})',
    if (spec.indexes.isNotEmpty)
      'indexes: <DVIndex>[${<String>[
        for (final DVStudioIndexSpec index in spec.indexes)
          'DVIndex(<String>[${index.fields.map(quote).join(', ')}]'
              '${index.unique ? ', unique: true' : ''})',
      ].join(', ')}]',
  ];
  out
    ..writeln('@DVModel(')
    ..writeln(<String>[for (final String a in arguments) '  $a,'].join('\n'))
    ..writeln(')')
    ..writeln("@pragma('vm:entry-point')")
    ..writeln('class const _${spec.model}({');
  final List<DVStudioFieldSpec> ordered = <DVStudioFieldSpec>[
    ...spec.fields.where((DVStudioFieldSpec f) => f.name == spec.key),
    ...spec.fields.where((DVStudioFieldSpec f) => f.name != spec.key),
  ];
  for (final DVStudioFieldSpec field in ordered) {
    final List<String> rules = <String>[
      if (field.min != null) 'min: ${_number(field.min!)}',
      if (field.max != null) 'max: ${_number(field.max!)}',
      if (field.minLength != null) 'minLength: ${field.minLength}',
      if (field.maxLength != null) 'maxLength: ${field.maxLength}',
      if (field.pattern != null) 'pattern: ${quote(field.pattern!)}',
    ];
    if (field.unique) out.writeln('  @DVModel.uniqueField()');
    if (rules.isNotEmpty) {
      out.writeln('  @DVModel.validate(${rules.join(', ')})');
    }
    final String type = field.type;
    out.writeln(
      field.nullable
          ? '  final $type ${field.name},'
          : '  required final $type ${field.name},',
    );
  }
  out.writeln('});');
  return out.toString();
}