buildWhereClass method

Class buildWhereClass(
  1. OrmBuildContext ctx
)

Generate

Implementation

Class buildWhereClass(OrmBuildContext ctx) {
  return Class((clazz) {
    var rc = ctx.buildContext.modelClassNameRecase;

    log.info('Generating ${rc.pascalCase}QueryWhere');

    clazz
      ..name = '${rc.pascalCase}QueryWhere'
      ..extend = refer('QueryWhere');

    // Build expressionBuilders getter
    clazz.methods.add(Method((m) {
      m
        ..name = 'expressionBuilders'
        ..returns = refer('List<SqlExpressionBuilder>')
        ..annotations.add(refer('override'))
        ..type = MethodType.getter
        ..body = Block((b) {
          var references =
              ctx.effectiveNormalFields.map((f) => refer(f.name));
          b.addExpression(literalList(references).returned);
        });
    }));

    var initializers = <Code>[];

    // Add builders for each field
    for (var field in ctx.effectiveNormalFields) {
      String? name = field.name;

      var args = <Expression>[];
      DartType type;
      Reference builderType;

      try {
        type = ctx.buildContext.resolveSerializedFieldType(field.name);
      } on StateError {
        type = field.type;
      }

      if (const TypeChecker.fromRuntime(int).isExactlyType(type) ||
          const TypeChecker.fromRuntime(double).isExactlyType(type) ||
          isSpecialId(ctx, field)) {
        var typeName = type.getDisplayString(withNullability: false);
        if (isSpecialId(ctx, field)) {
          typeName = 'int';
        }
        //log.fine('$name type = [$typeName]');
        builderType = TypeReference((b) => b
          ..symbol = 'NumericSqlExpressionBuilder'
          ..types.add(refer(typeName)));
      } else if (type is InterfaceType && type.element is EnumElement) {
        builderType = TypeReference((b) => b
          ..symbol = 'EnumSqlExpressionBuilder'
          ..types.add(convertTypeReference(type)));

        var question =
            type.nullabilitySuffix == NullabilitySuffix.question ? '?' : '';
        args.add(CodeExpression(Code('(v) => v$question.index as int')));
      } else if (const TypeChecker.fromRuntime(String).isExactlyType(type)) {
        builderType = refer('StringSqlExpressionBuilder');
      } else if (const TypeChecker.fromRuntime(bool).isExactlyType(type)) {
        builderType = refer('BooleanSqlExpressionBuilder');
      } else if (const TypeChecker.fromRuntime(DateTime)
          .isExactlyType(type)) {
        builderType = refer('DateTimeSqlExpressionBuilder');
      } else if (const TypeChecker.fromRuntime(Map)
          .isAssignableFromType(type)) {
        builderType = refer('MapSqlExpressionBuilder');
      } else if (const TypeChecker.fromRuntime(List)
          .isAssignableFromType(type)) {
        builderType = refer('ListSqlExpressionBuilder');

        // Detect relationship
      } else if (name.endsWith('Id')) {
        log.fine('Foreign Relationship detected = $name');
        var relation = ctx.relations[name.replaceAll('Id', '')];
        if (relation != null) {
          //if (relation?.type != RelationshipType.belongsTo) {
          //  continue;
          //} else {
          builderType = TypeReference((b) => b
            ..symbol = 'NumericSqlExpressionBuilder'
            ..types.add(refer('int')));
          //name = relation?.localKey;
          //}
        } else {
          log.warning(
              'Cannot generate ORM code for field ${field.name} of type ${field.type}');
          continue;
        }
      } else {
        log.warning(
            'Cannot generate ORM code for field ${field.name} of type ${field.type}');
        //ctx.relations.forEach((key, value) {
        //  log.fine('key: $key, value: $value');
        //});
        //throw UnsupportedError(
        //    'Cannot generate ORM code for field of type ${field.type.getDisplayString(withNullability: false)}.');
        continue;
      }

      clazz.fields.add(Field((b) {
        //log.fine('Field: $name, BuilderType: $builderType');

        b
          ..name = name
          ..modifier = FieldModifier.final$
          ..type = builderType;

        var literal = ctx.buildContext.resolveFieldName(field.name);
        if (literal != null) {
          //log.fine('Literal = ${field.name}  $literal');
          initializers.add(
            refer(field.name)
                .assign(builderType.newInstance([
                  refer('query'),
                  literalString(literal)
                ].followedBy(args)))
                .code,
          );
        } else {
          log.warning('Literal ${field.name} is null');
        }
      }));
    }

    // Now, just add a constructor that initializes each builder.
    clazz.constructors.add(Constructor((b) {
      b
        ..requiredParameters.add(Parameter((b) => b
          ..name = 'query'
          ..type = refer('${rc.pascalCase}Query')))
        ..initializers.addAll(initializers);
    }));
  });
}