Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Code Generation Attempt Unintendedly Triggered #1303

Open
jlambright opened this issue Feb 22, 2024 · 3 comments
Open

[Bug] Code Generation Attempt Unintendedly Triggered #1303

jlambright opened this issue Feb 22, 2024 · 3 comments
Assignees
Labels

Comments

@jlambright
Copy link

Given the abstract class (as well as a mixin version of the code) any sub-classes seem to trigger code generation attempts. However, I'm creating a wrapper class that consumes Built generated classes. This seems like buggy behavior. Either that, or it highlights the need for some kind of @built_ignore() annotation.

abstract class BuiltJsonSerializable<T extends Built<T, V>,
    V extends Builder<T, V>> {
  Serializer<T> get serializer;

  Map<String, dynamic> toJsonMap() => jsonDecode(toJsonString());

  String toJsonString() => standardSerializers.toJson<T>(serializer, toModel());

  T? toModel();

  T? modelFromJsonString(String serialized) {
    try {
      T? model = standardSerializers.fromJson<T>(serializer, serialized);
      return model;
    } on DeserializationError catch (e) {
      Get.log('[${T.toString()} DESERIALIZATION ERROR] ${e.toString()}');
      return null;
    }
  }
}
@davidmorgan
Copy link
Collaborator

The trigger for codegen is supposed to be implementing Built.

Could you please give an example of a class that triggers codegen that you expect to not trigger codegen?

Thanks :)

@jlambright
Copy link
Author

Below you'll see one of the sub-classes. When I run dart run build_runner build (as I do have some codegen in this project), it throws the following errors.

[SEVERE] built_value_generator:built_value on lib/data/wrappers/wrappers.dart:
Error in BuiltValueGenerator for /dsk360_command/lib/data/wrappers/wrappers.dart.
Please make the following changes to use built_value serialization:
<I'm skipping a bunch of errors since they're almost all the same.>
10. Declare TicketWrapper.serializer as: static Serializer<TicketWrapper> get serializer => _$ticketWrapperSerializer; got @override Serializer<TicketModel> get serializer => _$ticketWrapperSerializer;
part of 'wrappers.dart';

TicketWrapper ticketFromJson(String str) => TicketWrapper.fromJsonString(str);
TicketModel? ticketToModel(TicketWrapper data) => data.toModel();
String ticketToJson(TicketWrapper data) => standardSerializers
    .toJson<TicketModel>(TicketModel.serializer, data.toModel());

List<TicketWrapper> ticketsFromModelList(List<TicketModel>? tickets) {
  if (tickets == null) {
    return [];
  } else {
    return tickets
        .asMap()
        .entries
        .map((entry) => TicketWrapper.fromModel(
            ticketModel: entry.value, number: entry.key))
        .toList();
  }
}

Map<String, TicketWrapper> ticketsMapFromTicketModelList(
    List<TicketModel>? tickets) {
  if (tickets == null) {
    return <String, TicketWrapper>{};
  } else {
    return Map.fromEntries(
        tickets.map<MapEntry<String, TicketWrapper>>((ticketModel) {
      final TicketWrapper ticket =
          TicketWrapper.fromModel(ticketModel: ticketModel);
      return MapEntry(ticket.uuid, ticket);
    }));
  }
}

Map<String, TicketWrapper> ticketsMapFromList(List<TicketWrapper>? tickets) =>
    {for (TicketWrapper ticket in tickets ?? []) ticket.uuid: ticket};

List<TicketModel?> ticketWrapperListToModel(
    List<TicketWrapper> ticketWrappers) {
  return ticketWrappers
      .map((ticketWrapper) => ticketWrapper.toModel())
      .toList();
}

List<Map<String, dynamic>> toJsonMapTicketList(List<TicketWrapper> tickets) =>
    tickets.map((ticket) => ticket.toJsonMap()).toList();

class TicketWrapper
    extends BuiltJsonSerializable<TicketModel, TicketModelBuilder> {
  @override
  Serializer<TicketModel> get serializer => TicketModel.serializer;

  late final int dataHash;

  late final String uuid;
  late final DateTime lastModified;
  late final ContactWrapper contact;
  late final TicketStatusEnum status;
  late final Map<String, NoteWrapper> notes;
  List<NoteWrapper> get notesList => notes.values.toList();
  List<String> get notesUuids => notes.keys.toList();

  late final Map<String, TagWrapper> tags;
  List<TagWrapper> get tagList => tags.values.toList();
  List<String> get tagUuids => tags.keys.toList();

  List<String> get tagNames => [for (TagWrapper tag in tagList) tag.name];
  bool hasTagByUuid(String tagUuid) => tagUuids.contains(tagUuid);

  bool get isMaintenance => tagNames.contains("Maintenance");
  bool get isSecurity => tagNames.contains("Security");

  bool get hasImageUrls => contact.hasLatestUrls;
  String? get latestImageUrl => contact.latestImageUrl;
  List<String>? get latestImageUrls => contact.latestImageUrls;
  BoundingSphereWrapper get boundingSphere => contact.boundingSphere;

  @override
  int get hashCode => toJsonString().hashCode;

  TicketWrapper(
      {required this.uuid,
      required this.contact,
      DateTime? lastModified,
      this.status = TicketStatusEnum.unknown,
      this.notes = const {},
      this.tags = const {}})
      : lastModified = lastModified ?? epochTime;

  TicketWrapper.fromModel({TicketModel? ticketModel, int? number}) {
    uuid = ticketModel?.uuid ?? _uuid.v4();
    lastModified = ticketModel?.lastModified ?? epochTime;
    contact = ContactWrapper.fromModel(ticketModel?.contact);
    status = TicketStatusEnum.fromDkpQualityEnum(ticketModel?.status);
    tags = fromTagModelList(ticketModel?.tags.toList());
    notes = fromNoteModelList(ticketModel?.notes?.toList());
  }

  TicketWrapper.createEmptyWrapper() {
    uuid = _uuid.v4();
    lastModified = epochTime;
    contact = ContactWrapper.createEmptyWrapper();
    status = TicketStatusEnum.unknown;
    tags = {};
    notes = {};
  }

  TicketWrapper.fromJsonString(String serialized) {
    try {
      TicketModel? ticketModel =
          standardSerializers.fromJson<TicketModel>(serializer, serialized);
      TicketWrapper.fromModel(ticketModel: ticketModel);
    } on AssertionError {
      throw ArgumentError.notNull(serialized);
    }
  }

  @override
  Map<String, dynamic> toJsonMap() => jsonDecode(toJsonString());

  @override
  TicketModel? toModel() {
    return modelFromJsonString(jsonEncode({
      "uuid": uuid,
      "lastModified": lastModified,
      "contact": contact.toModel(),
      "status": status == TicketStatusEnum.unknown ? "Open" : status.value,
      "notes": noteWrapperListToModel(notesList),
      "tags": tagWrapperListToModel(tagList)
    }));
  }

  @override
  bool operator ==(Object other) {
    return super.hashCode == other.hashCode;
  }
}

@davidmorgan
Copy link
Collaborator

Oh, I see the problem.

The serializer codegen triggers for a class if any of its supertypes starts with Built, and there is a field called serializer.

I have no idea why it's like this, the value type generation only triggers if the interface is exactly Built.

That looks like it should just be fixed, until then you can work around by renaming your class to anything that doesn't start with Built or renaming serializer. (Whoops).

@davidmorgan davidmorgan self-assigned this Feb 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants