coderForField method

  1. @override
String? coderForField(
  1. FieldElement field,
  2. SharedChecker<Model> checker,
  3. {required bool wrappedInFuture,
  4. required Sqlite fieldAnnotation}
)

Produces serializing or deserializing method given a field and checker.

The assignment (data['my_field']: in serializers or myField: in deserializers) is automatically injected by the superclass and should not be included in the output of the coder.

To simplify checking, Futures are unwrapped before they get to this method. If the type was originally a future, wrappedInFuture is true. For example, Future<List<int>> will be an iterable according to the checker and wrappedInFuture will be true.

Implementation

@override
String? coderForField(field, checker, {required wrappedInFuture, required fieldAnnotation}) {
  final name = providerNameForField(fieldAnnotation.name, checker: checker);
  final fieldValue = serdesValueForField(field, fieldAnnotation.name!, checker: checker);
  if (name == InsertTable.PRIMARY_KEY_COLUMN) {
    throw InvalidGenerationSourceError(
      'Field named `${InsertTable.PRIMARY_KEY_COLUMN}` conflicts with primary key',
      todo: 'Rename the field from ${InsertTable.PRIMARY_KEY_COLUMN}',
      element: field,
    );
  }

  if (fieldAnnotation.ignoreTo) return null;

  if (fieldAnnotation.columnType != null) {
    return fieldValue;
  }

  // DateTime
  if (checker.isDateTime) {
    final nullableSuffix = checker.isNullable ? '?' : '';
    return '$fieldValue$nullableSuffix.toIso8601String()';

    // bool
  } else if (checker.isBool) {
    return _boolForField(fieldValue, checker.isNullable);

    // double, int, num, String
  } else if (checker.isDartCoreType) {
    return fieldValue;

    // Iterable
  } else if (checker.isIterable) {
    final argTypeChecker = checkerForType(checker.argType);

    // Iterable<enum>
    if (argTypeChecker.isEnum) {
      final nullablePrefix = checker.isNullable ? '?' : '';
      final nullableDefault = checker.isNullable ? ' ?? []' : '';
      final serializeMethod = argTypeChecker.enumSerializeMethod(providerName);
      final serializedValue = serializeMethod != null
          ? 's.$serializeMethod()'
          : fieldAnnotation.enumAsString
              ? "s.name"
              : '${SharedChecker.withoutNullability(checker.argType)}.values.indexOf(s)';

      return '''
        jsonEncode($fieldValue$nullablePrefix.map((s) =>
          $serializedValue
        ).toList()$nullableDefault)
      ''';
    }

    // Iterable<Future<bool>>, Iterable<Future<DateTime>>, Iterable<Future<double>>,
    // Iterable<Future<int>>, Iterable<Future<num>>, Iterable<Future<String>>, Iterable<Future<Map>>
    if (checker.isArgTypeAFuture) {
      if (checker.isSerializable && !checker.isArgTypeASibling) {
        // Iterable<Future<bool>>
        final wrappedValue =
            checker.isBool ? _boolForField(fieldValue, fieldAnnotation.nullable) : fieldValue;

        return 'jsonEncode(await Future.wait<${argTypeChecker.unFuturedArgType}>($wrappedValue) ?? [])';
      }
    }

    // Set<any>
    // jsonEncode can't convert LinkedHashSet
    if (checker.isSet && !checker.isArgTypeASibling) {
      return checker.isNullable
          ? '$fieldValue == null ? null : jsonEncode($fieldValue.toList())'
          : 'jsonEncode($fieldValue.toList())';
    }

    // Iterable<bool>
    if (argTypeChecker.isBool) {
      return 'jsonEncode($fieldValue.map((b) => ${_boolForField('b', fieldAnnotation.nullable)}).toList())';
    }

    // Iterable<DateTime>, Iterable<double>, Iterable<int>, Iterable<num>, Iterable<String>, Iterable<Map>
    if (argTypeChecker.isDartCoreType || argTypeChecker.isMap) {
      return checker.isNullable
          ? '$fieldValue == null ? null : jsonEncode($fieldValue)'
          : 'jsonEncode($fieldValue)';
    }

    // Iterable<toJson>
    if (argTypeChecker.toJsonMethod != null) {
      final serializedValue = 'jsonEncode($fieldValue)';
      return checker.isNullable
          ? '$fieldValue != null ? $serializedValue : null'
          : serializedValue;
    }

    // SqliteModel, Future<SqliteModel>
  } else if (checker.isSibling) {
    final instance = wrappedInFuture ? '(await $fieldValue)' : fieldValue;
    final nullabilitySuffix = checker.isUnFuturedTypeNullable || checker.isNullable ? '!' : '';
    final upsertMethod = '''
      $instance$nullabilitySuffix.${InsertTable.PRIMARY_KEY_FIELD} ??
      await provider.upsert<${SharedChecker.withoutNullability(checker.unFuturedType)}>(
        $instance$nullabilitySuffix, repository: repository
      )''';

    if (checker.isUnFuturedTypeNullable) {
      return '$instance != null ? $upsertMethod : null';
    }

    return upsertMethod;

    // enum
  } else if (checker.isEnum) {
    final nullabilitySuffix = checker.isNullable ? '?' : '';
    final serializeMethod = checker.enumSerializeMethod(providerName);
    if (serializeMethod != null) {
      return "$fieldValue$nullabilitySuffix.$serializeMethod()";
    }

    if (fieldAnnotation.enumAsString) {
      return "$fieldValue$nullabilitySuffix.name";
    }

    if (checker.isNullable) {
      return '$fieldValue != null ? ${SharedChecker.withoutNullability(field.type)}.values.indexOf($fieldValue!) : null';
    }

    return '${SharedChecker.withoutNullability(field.type)}.values.indexOf($fieldValue)';

    // Map
  } else if (checker.isMap) {
    final nullableSuffix = checker.isNullable ? ' ?? {}' : '';
    return 'jsonEncode($fieldValue$nullableSuffix)';
  } else if (checker.toJsonMethod != null) {
    final nullableSuffix = checker.isNullable ? '!' : '';
    final output = 'jsonEncode($fieldValue$nullableSuffix.toJson())';
    if (checker.isNullable) {
      return '$fieldValue != null ? $output : null';
    }
    return output;
  }

  return null;
}