generateDart function

String generateDart(
  1. List<Map<String, dynamic>> collections, {
  2. String header = '// AUTO-GENERATED by `dart run vaultbase:types`. ' 'Do not edit.\n',
})

Dart source for the collections of a schema snapshot: per collection a XRecord extends RecordModel with a getter per field, an XCreate (base and auth collections) with toJson(), and vb.collections.<name>.

Implementation

String generateDart(List<Map<String, dynamic>> collections,
    {String header = '// AUTO-GENERATED by `dart run vaultbase:types`. '
        'Do not edit.\n'}) {
  final classes = <String>[];
  final accessors = <String>[];
  final accessorNames = {
    'vb',
    'hashCode',
    'runtimeType',
    'toString',
    'noSuchMethod'
  };
  final classNames = {'Vaultbase'};
  var usesGeo = false;
  var usesGroupDate = false;
  // Group classes are named after their collection and field; kept clear of
  // every record and create class, and of the library's own.
  final typeNames = {
    'Vaultbase', 'VaultbaseCollections', 'RecordModel', 'Collection', //
    'Singleton',
  };
  final bases = <Map<String, dynamic>, String>{};
  for (final col in collections) {
    final name = col['name'];
    if (name is! String || !_collectionName.hasMatch(name)) continue;
    final base = _unique(_pascal(name), classNames);
    bases[col] = base;
    typeNames.addAll(['${base}Record', '${base}Create', '${base}Params']);
  }

  for (final col in collections) {
    final base = bases[col];
    if (base == null) continue;
    final name = col['name'] as String;
    final type = col['type'] as String? ?? 'base';
    final fields = [
      for (final f in _parseFields(col['fields']))
        if (_keepField(f, type)) f
    ];
    for (final f in fields.where((f) => f.type == 'group')) {
      final cls = _unique(
          '$base${_pascal(f.name)}${f.groupList ? 'Item' : ''}', typeNames);
      f.groupClass = cls;
      classes.add(_emitGroup(cls, name, f));
      usesGroupDate = usesGroupDate || f.subfields.any((s) => s.type == 'date');
    }
    final names = _fieldNames(
        fields, type == 'auth' ? _recordMembers : _recordMembersNoAuth);
    usesGeo = usesGeo || fields.any((f) => f.type == 'geoPoint');

    final accessor = _unique(_safeIdent(_camel(name)), accessorNames);
    classes.add(_emitRecord('${base}Record', name, type, fields, names));
    if (type == 'base' || type == 'auth') {
      classes.add(_emitCreate(
          '${base}Create',
          name,
          type,
          [
            for (final f in fields)
              if (f.type != 'autodate') f
          ],
          names,
          accessor));
    }

    if (type == 'view') {
      final params = _viewParams(col['view_params']);
      if (params.isNotEmpty) {
        classes.add(_emitParams('${base}Params', name, params, accessor));
      }
    }

    final quoted = _dartString(name);
    accessors.add(type == 'singleton'
        ? '  Singleton<${base}Record> get $accessor =>\n'
            '      vb.singletonOf($quoted, ${base}Record.fromJson);'
        : '  Collection<${base}Record> get $accessor =>\n'
            '      vb.collectionOf($quoted, ${base}Record.fromJson);');
  }

  final parts = [
    header.trimRight(),
    '// ignore_for_file: type=lint',
    '',
    "import 'package:vaultbase/vaultbase.dart';",
    '',
    classes.join('\n\n'),
    '',
    '/// Each collection with its record type: `vb.collections.posts.list()`.',
    'class VaultbaseCollections {',
    '  const VaultbaseCollections(this.vb);',
    '',
    '  final Vaultbase vb;',
    if (accessors.isNotEmpty) ...['', accessors.join('\n\n')],
    '}',
    '',
    'extension VaultbaseSchema on Vaultbase {',
    '  VaultbaseCollections get collections => VaultbaseCollections(this);',
    '}',
    if (usesGeo) ...[
      '',
      '({double lat, double lng})? _geoPoint(Object? v) => v is Map &&',
      "        v['lat'] is num &&",
      "        v['lng'] is num",
      "    ? (lat: (v['lat'] as num).toDouble(), "
          "lng: (v['lng'] as num).toDouble())",
      '    : null;',
    ],
    if (usesGroupDate) ...[
      '',
      'DateTime? _groupDate(Object? v) => v is num',
      '    ? DateTime.fromMillisecondsSinceEpoch(v.toInt() * 1000, isUtc: true)',
      '    : null;',
    ],
    '',
  ];
  return parts.join('\n');
}